[libcxx-commits] [libcxx] 3d3103b - [libcxx][ranges] add views::join adaptor object. added test coverage to join_view
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Apr 21 04:12:06 PDT 2022
Author: Hui Xie
Date: 2022-04-21T13:10:46+02:00
New Revision: 3d3103b733d4346d583a3ada3aabdaa9de4f0446
URL: https://github.com/llvm/llvm-project/commit/3d3103b733d4346d583a3ada3aabdaa9de4f0446
DIFF: https://github.com/llvm/llvm-project/commit/3d3103b733d4346d583a3ada3aabdaa9de4f0446.diff
LOG: [libcxx][ranges] add views::join adaptor object. added test coverage to join_view
- added views::join adaptor object
- added test for the adaptor object
- fixed some join_view's tests. e.g iter_swap test
- added some negative tests for join_view to test that operations do not exist when constraints aren't met
- added tests that locks down issues that were already addressed in previous change
- LWG3500 `join_view::iterator::operator->()` is bogus
- LWG3313 `join_view::iterator::operator--` is incorrectly constrained
- LWG3517 `join_view::iterator`'s `iter_swap` is underconstrained
- P2328R1 join_view should join all views of ranges
- fixed some issues in join_view and added tests
- LWG3535 `join_view::iterator::iterator_category` and `::iterator_concept` lie
- LWG3474 Nesting ``join_views`` is broken because of CTAD
- added tests for an LWG issue that isn't resolved in the standard yet, but the previous code has workaround.
- LWG3569 Inner iterator not default_initializable
Reviewed By: #libc, var-const
Spies: var-const, libcxx-commits
Differential Revision: https://reviews.llvm.org/D123466
Added:
libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp
Modified:
libcxx/docs/Status/Cxx20Issues.csv
libcxx/docs/Status/Cxx2bIssues.csv
libcxx/docs/Status/RangesIssues.csv
libcxx/include/__ranges/join_view.h
libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.join.view/types.h
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index 9d34066e50d9d..d307fb17a4359 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -234,7 +234,7 @@
"`3304 <https://wg21.link/LWG3304>`__","Allocate functions of ``std::polymorphic_allocator``\ should require ``[[nodiscard]]``\ ","Prague","",""
"`3307 <https://wg21.link/LWG3307>`__","``std::allocator<void>().allocate(n)``\ ","Prague","",""
"`3310 <https://wg21.link/LWG3310>`__","Replace ``SIZE_MAX``\ with ``numeric_limits<size_t>::max()``\ ","Prague","",""
-"`3313 <https://wg21.link/LWG3313>`__","``join_view::iterator::operator--``\ is incorrectly constrained","Prague","","","|ranges|"
+"`3313 <https://wg21.link/LWG3313>`__","``join_view::iterator::operator--``\ is incorrectly constrained","Prague","|Complete|","14.0","|ranges|"
"`3314 <https://wg21.link/LWG3314>`__","Is stream insertion behavior locale dependent when ``Period::type``\ is ``micro``\ ?","Prague","","","|chrono|"
"`3315 <https://wg21.link/LWG3315>`__","Correct Allocator Default Behavior","Prague","",""
"`3316 <https://wg21.link/LWG3316>`__","Correctly define epoch for ``utc_clock``\ / ``utc_timepoint``\ ","Prague","","","|chrono|"
diff --git a/libcxx/docs/Status/Cxx2bIssues.csv b/libcxx/docs/Status/Cxx2bIssues.csv
index 7adfbf251ceb7..9047a63b6dd65 100644
--- a/libcxx/docs/Status/Cxx2bIssues.csv
+++ b/libcxx/docs/Status/Cxx2bIssues.csv
@@ -44,7 +44,7 @@
"`3467 <https://wg21.link/LWG3467>`__","``bool`` can't be an integer-like type","November 2020","|Complete|","14.0"
"`3472 <https://wg21.link/LWG3472>`__","``counted_iterator`` is missing preconditions","November 2020","|Complete|","14.0","|ranges|"
"`3473 <https://wg21.link/LWG3473>`__","Normative encouragement in non-normative note","November 2020","|Nothing To Do|","","|format|"
-"`3474 <https://wg21.link/LWG3474>`__","Nesting ``join_views`` is broken because of CTAD","November 2020","","","|ranges|"
+"`3474 <https://wg21.link/LWG3474>`__","Nesting ``join_views`` is broken because of CTAD","November 2020","|Complete|","15.0","|ranges|"
"`3476 <https://wg21.link/LWG3476>`__","``thread`` and ``jthread`` constructors require that the parameters be move-constructible but never move construct the parameters","November 2020","",""
"`3477 <https://wg21.link/LWG3477>`__","Simplify constraints for ``semiregular-box``","November 2020","","","|ranges|"
"`3482 <https://wg21.link/LWG3482>`__","``drop_view``'s const begin should additionally require ``sized_range``","November 2020","|Complete|","14.0","|ranges|"
@@ -56,7 +56,7 @@
"`3492 <https://wg21.link/LWG3492>`__","Minimal improvements to ``elements_view::iterator``","February 2021","","","|ranges|"
"`3494 <https://wg21.link/LWG3494>`__","Allow ranges to be conditionally borrowed","February 2021","Superseded by `P2017R1 <https://wg21.link/P2017R1>`__","","|ranges|"
"`3495 <https://wg21.link/LWG3495>`__","``constexpr launder`` makes pointers to inactive members of unions usable","February 2021","|Nothing To Do|",""
-"`3500 <https://wg21.link/LWG3500>`__","``join_view::iterator::operator->()`` is bogus","February 2021","","","|ranges|"
+"`3500 <https://wg21.link/LWG3500>`__","``join_view::iterator::operator->()`` is bogus","February 2021","|Complete|","14.0","|ranges|"
"`3502 <https://wg21.link/LWG3502>`__","``elements_view`` should not be allowed to return dangling reference","February 2021","","","|ranges|"
"`3505 <https://wg21.link/LWG3505>`__","``split_view::outer-iterator::operator++`` misspecified","February 2021","","","|ranges|"
"","","","",""
@@ -68,7 +68,7 @@
`3462 <https://wg21.link/LWG3462>`__,"§[formatter.requirements]: Formatter requirements forbid use of ``fc.arg()``","June 2021","","","|format|"
`3481 <https://wg21.link/LWG3481>`__,"``viewable_range`` mishandles lvalue move-only views","June 2021","Superseded by `P2415R2 <https://wg21.link/P2415R2>`__","","|ranges|"
`3506 <https://wg21.link/LWG3506>`__,"Missing allocator-extended constructors for ``priority_queue``","June 2021","|Complete|","14.0"
-`3517 <https://wg21.link/LWG3517>`__,"``join_view::iterator``'s ``iter_swap`` is underconstrained","June 2021","","","|ranges|"
+`3517 <https://wg21.link/LWG3517>`__,"``join_view::iterator``'s ``iter_swap`` is underconstrained","June 2021","|Complete|","14.0","|ranges|"
`3518 <https://wg21.link/LWG3518>`__,"Exception requirements on char trait operations unclear","June 2021","|Nothing To Do|",""
`3519 <https://wg21.link/LWG3519>`__,"Incomplete synopses for ``<random>`` classes","June 2021","",""
`3520 <https://wg21.link/LWG3520>`__,"``iter_move`` and ``iter_swap`` are inconsistent for ``transform_view::iterator``","June 2021","|Complete|","14.0","|ranges|"
@@ -112,7 +112,7 @@
`3470 <https://wg21.link/LWG3470>`__,"``convertible-to-non-slicing`` seems to reject valid case","October 2021","|Complete|","14.0","|ranges|"
`3480 <https://wg21.link/LWG3480>`__,"``directory_iterator`` and ``recursive_directory_iterator`` are not C++20 ranges","October 2021","|Complete|","14.0","|ranges|"
`3498 <https://wg21.link/LWG3498>`__,"Inconsistent ``noexcept``-specifiers for ``basic_syncbuf``","October 2021","",""
-`3535 <https://wg21.link/LWG3535>`__,"``join_view::iterator::iterator_category`` and ``::iterator_concept`` lie","October 2021","","","|ranges|"
+`3535 <https://wg21.link/LWG3535>`__,"``join_view::iterator::iterator_category`` and ``::iterator_concept`` lie","October 2021","|Complete|","15.0","|ranges|"
`3554 <https://wg21.link/LWG3554>`__,"``chrono::parse`` needs ``const charT*`` and ``basic_string_view<charT>`` overloads","October 2021","","","|chrono|"
`3557 <https://wg21.link/LWG3557>`__,"The ``static_cast`` expression in ``convertible_to`` has the wrong operand","October 2021","|Complete|","14.0"
`3559 <https://wg21.link/LWG3559>`__,"Semantic requirements of ``sized_range`` is circular","October 2021","|Nothing To Do|","","|ranges|"
diff --git a/libcxx/docs/Status/RangesIssues.csv b/libcxx/docs/Status/RangesIssues.csv
index 728742d5b9d6c..12d40e32fa734 100644
--- a/libcxx/docs/Status/RangesIssues.csv
+++ b/libcxx/docs/Status/RangesIssues.csv
@@ -24,7 +24,7 @@
`P2106R0 <https://wg21.link/P2106R0>`__,Range Algorithm Result Types,,
`P2325R3 <https://wg21.link/P2325R3>`__,Views should not be required to be default constructible ,,
-`P2328R1 <https://wg21.link/P2328R1>`__,join_view should join all views of ranges,,
+`P2328R1 <https://wg21.link/P2328R1>`__,join_view should join all views of ranges,|Complete|,14.0
`P2210R2 <https://wg21.link/P2210R2>`__,Superior String Splitting,,
`P2281R1 <https://wg21.link/P2281R1>`__,Clarifying range adaptor objects,|Complete|,14.0
`P2367R0 <https://wg21.link/P2367R0>`__,Remove misuses of list-initialization from Clause 24,,
diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h
index b95db5bb30fe6..b6fcce95aeda9 100644
--- a/libcxx/include/__ranges/join_view.h
+++ b/libcxx/include/__ranges/join_view.h
@@ -23,10 +23,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 <optional>
#include <type_traits>
@@ -52,7 +50,8 @@ namespace ranges {
using _InnerC = typename iterator_traits<iterator_t<range_reference_t<_View>>>::iterator_category;
using iterator_category = _If<
- derived_from<_OuterC, bidirectional_iterator_tag> && derived_from<_InnerC, bidirectional_iterator_tag>,
+ derived_from<_OuterC, bidirectional_iterator_tag> && derived_from<_InnerC, bidirectional_iterator_tag> &&
+ common_range<range_reference_t<_View>>,
bidirectional_iterator_tag,
_If<
derived_from<_OuterC, forward_iterator_tag> && derived_from<_InnerC, forward_iterator_tag>,
@@ -211,7 +210,8 @@ namespace ranges {
public:
using iterator_concept = _If<
- __ref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<range_reference_t<_Base>>,
+ __ref_is_glvalue && bidirectional_range<_Base> && bidirectional_range<range_reference_t<_Base>> &&
+ common_range<range_reference_t<_Base>>,
bidirectional_iterator_tag,
_If<
__ref_is_glvalue && forward_range<_Base> && forward_range<range_reference_t<_Base>>,
@@ -345,7 +345,22 @@ namespace ranges {
template<class _Range>
explicit join_view(_Range&&) -> join_view<views::all_t<_Range>>;
-
+
+namespace views {
+namespace __join_view {
+struct __fn : __range_adaptor_closure<__fn> {
+ template<class _Range>
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI
+ constexpr auto operator()(_Range&& __range) const
+ noexcept(noexcept(join_view<all_t<_Range&&>>(std::forward<_Range>(__range))))
+ -> decltype( join_view<all_t<_Range&&>>(std::forward<_Range>(__range)))
+ { return join_view<all_t<_Range&&>>(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
index 0000000000000..c28cc8413e017
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/adaptor.pass.cpp
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <ranges>
+
+#include <cassert>
+#include <type_traits>
+
+#include "types.h"
+
+struct MoveOnlyOuter : SimpleForwardCommonOuter<ForwardCommonInner> {
+ using SimpleForwardCommonOuter<ForwardCommonInner>::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 <class View, class T>
+concept CanBePiped = requires(View&& view, T&& t) {
+ { std::forward<View>(view) | std::forward<T>(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::ranges::ref_view<ForwardCommonInner[3]>>;
+ std::same_as<Result> decltype(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<MoveOnlyOuter>;
+ std::same_as<Result> decltype(auto) v = std::views::join(MoveOnlyOuter{inners});
+ assert(std::ranges::next(v.begin(), 9) == v.end());
+ assert(&(*v.begin()) == buffer1);
+
+ static_assert(std::invocable<decltype(std::views::join), MoveOnlyOuter>);
+ static_assert(!std::invocable<decltype(std::views::join), MoveOnlyOuter&>);
+ }
+
+ {
+ // LWG3474 Nesting `join_views` is broken because of CTAD
+ // views::join(join_view) should join the view instead of calling copy constructor
+ auto jv = std::views::join(nested);
+ ASSERT_SAME_TYPE(std::ranges::range_reference_t<decltype(jv)>, Foo(&)[3]);
+
+ auto jv2 = std::views::join(jv);
+ ASSERT_SAME_TYPE(std::ranges::range_reference_t<decltype(jv2)>, 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::ranges::ref_view<ForwardCommonInner[3]>>;
+ std::same_as<Result> decltype(auto) v = inners | std::views::join;
+ assert(std::ranges::next(v.begin(), 9) == v.end());
+ assert(&(*v.begin()) == buffer1);
+ static_assert(CanBePiped<decltype((inners)), decltype((std::views::join))>);
+ }
+
+ {
+ // Test `move-only-view | views::join`
+ ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3};
+ using Result = std::ranges::join_view<MoveOnlyOuter>;
+ std::same_as<Result> decltype(auto) v = MoveOnlyOuter{inners} | std::views::join;
+ assert(std::ranges::next(v.begin(), 9) == v.end());
+ assert(&(*v.begin()) == buffer1);
+
+ static_assert(CanBePiped<MoveOnlyOuter, decltype((std::views::join))>);
+ static_assert(!CanBePiped<MoveOnlyOuter&, decltype((std::views::join))>);
+ }
+
+ {
+ // LWG3474 Nesting `join_views` is broken because of CTAD
+ // join_view | views::join should join the view instead of calling copy constructor
+ auto jv = nested | std::views::join | std::views::join;
+ ASSERT_SAME_TYPE(std::ranges::range_reference_t<decltype(jv)>, Foo&);
+
+ assert(&(*jv.begin()) == &nested[0][0][0]);
+ static_assert(CanBePiped<decltype((nested)), decltype((std::views::join))>);
+ }
+
+ {
+ // Test `adaptor | views::join`
+ auto join_twice = std::views::join | std::views::join;
+ auto jv = nested | join_twice;
+ ASSERT_SAME_TYPE(std::ranges::range_reference_t<decltype(jv)>, Foo&);
+
+ assert(&(*jv.begin()) == &nested[0][0][0]);
+ static_assert(CanBePiped<decltype((nested)), decltype((join_twice))>);
+ }
+
+ {
+ static_assert(!CanBePiped<int, decltype((std::views::join))>);
+ static_assert(!CanBePiped<Foo, decltype((std::views::join))>);
+ static_assert(!CanBePiped<int(&)[2], decltype((std::views::join))>);
+ static_assert(CanBePiped<int(&)[2][2], decltype((std::views::join))>);
+ }
+
+ 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
index 10d18a8f92abc..8482b780f16cf 100644
--- 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<const V> &&
+// is_reference_v<range_reference_t<const V>>;
#include <cassert>
#include <ranges>
-#include "test_macros.h"
#include "types.h"
struct NonSimpleParentView : std::ranges::view_base {
@@ -29,6 +30,16 @@ struct SimpleParentView : std::ranges::view_base {
const ChildView* end() const;
};
+struct ConstNotRange : std::ranges::view_base {
+ const ChildView* begin();
+ const ChildView* end();
+};
+static_assert(std::ranges::range<ConstNotRange>);
+static_assert(!std::ranges::range<const ConstNotRange>);
+
+template <class T>
+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}};
@@ -39,49 +50,61 @@ constexpr bool test() {
}
{
- CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 1), CopyableChild(buffer[3], 0)};
+ CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 1),
+ CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView{children});
assert(*jv.begin() == 1111);
}
+
// Parent is empty.
{
- CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
+ CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]),
+ CopyableChild(buffer[3])};
std::ranges::join_view jv(ParentView(children, 0));
assert(jv.begin() == jv.end());
}
+
// Parent size is one.
{
CopyableChild children[1] = {CopyableChild(buffer[0])};
std::ranges::join_view jv(ParentView(children, 1));
assert(*jv.begin() == 1111);
}
+
// Parent and child size is one.
{
CopyableChild children[1] = {CopyableChild(buffer[0], 1)};
std::ranges::join_view jv(ParentView(children, 1));
assert(*jv.begin() == 1111);
}
+
// Parent size is one child is empty
{
CopyableChild children[1] = {CopyableChild(buffer[0], 0)};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.begin() == jv.end());
}
+
// Has all empty children.
{
- CopyableChild children[4] = {CopyableChild(buffer[0], 0), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)};
+ CopyableChild children[4] = {CopyableChild(buffer[0], 0), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0),
+ CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView{children});
assert(jv.begin() == jv.end());
}
+
// First child is empty, others are not.
{
- CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)};
+ CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0),
+ CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView{children});
assert(*jv.begin() == 1111);
}
+
// Last child is empty, others are not.
{
- CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 4), CopyableChild(buffer[2], 4), CopyableChild(buffer[3], 0)};
+ CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 4), CopyableChild(buffer[2], 4),
+ CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView{children});
assert(*jv.begin() == 1111);
}
@@ -94,20 +117,33 @@ constexpr bool test() {
{
const std::ranges::join_view jv(buffer);
assert(*jv.begin() == 1111);
+ static_assert(HasConstBegin<decltype(jv)>);
+ }
+
+ // !input_range<const V>
+ {
+ std::ranges::join_view jv{ConstNotRange{}};
+ static_assert(!HasConstBegin<decltype(jv)>);
+ }
+
+ // !is_reference_v<range_reference_t<const V>>
+ {
+ auto innerRValueRange = std::views::iota(0, 5) | std::views::transform([](int) { return ChildView{}; });
+ static_assert(!std::is_reference_v<std::ranges::range_reference_t<const decltype(innerRValueRange)>>);
+ std::ranges::join_view jv{innerRValueRange};
+ static_assert(!HasConstBegin<decltype(jv)>);
}
// !simple-view<V>
{
std::ranges::join_view<NonSimpleParentView> jv;
- static_assert(!std::same_as<decltype(jv.begin()),
- decltype(std::as_const(jv).begin())>);
+ static_assert(!std::same_as<decltype(jv.begin()), decltype(std::as_const(jv).begin())>);
}
// simple-view<V> && is_reference_v<range_reference_t<V>>;
{
std::ranges::join_view<SimpleParentView> jv;
- static_assert(std::same_as<decltype(jv.begin()),
- decltype(std::as_const(jv).begin())>);
+ static_assert(std::same_as<decltype(jv.begin()), decltype(std::as_const(jv).begin())>);
}
return true;
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
index 95c1ef3c399a6..7a7d31dd7203b 100644
--- 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 @@ struct BorrowedRange {
template<>
inline constexpr bool std::ranges::enable_borrowed_range<BorrowedRange> = true;
+struct NestedChildren : std::ranges::view_base {
+ View* begin() const;
+ View* end() const;
+};
+
void testCTAD() {
View v;
Range r;
@@ -66,4 +71,13 @@ void testCTAD() {
decltype(std::ranges::join_view(std::move(br))),
std::ranges::join_view<std::ranges::owning_view<BorrowedRange>>
>);
+
+ 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
index a5a24570c776c..a89ade02628a8 100644
--- 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,13 +14,26 @@
#include <cassert>
#include <ranges>
-#include "test_macros.h"
#include "types.h"
+struct DefaultView : std::ranges::view_base {
+ int i; // deliberately uninitialised
+
+ ChildView* begin() const;
+ ChildView* end() const;
+};
constexpr bool test() {
- std::ranges::join_view<ParentView<ChildView>> jv;
- assert(std::move(jv).base().ptr_ == globalChildren);
+ {
+ std::ranges::join_view<ParentView<ChildView>> jv;
+ assert(std::move(jv).base().ptr_ == globalChildren);
+ }
+
+ // Default constructor should value initialise underlying view
+ {
+ std::ranges::join_view<DefaultView> jv;
+ assert(jv.base().i == 0);
+ }
static_assert( std::default_initializable<std::ranges::join_view<ParentView<ChildView>>>);
static_assert(!std::default_initializable<std::ranges::join_view<CopyableParent>>);
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
index 9ac91e6bd016e..d36514a81df8f 100644
--- 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,44 +19,217 @@
#include "test_macros.h"
#include "types.h"
+template <class T>
+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<true> |iterator<true>|
+// | 2 | Y | Y | Y | Y | Y | N |sentinel<true> |sentinel<true>|
+// | 3 | Y | Y | Y | Y | N | Y |sentinel<true> |sentinel<true>|
+// | 4 | Y | Y | Y | N | Y | Y |sentinel<true> | - |
+// | 5 | Y | Y | N | Y | Y | Y |sentinel<true> |sentinel<true>|
+// | 6 | Y | N | Y | Y | Y | Y |sentinel<true> |sentinel<true>|
+// | 7 | N | Y | Y | Y | Y | Y |iterator<false>|iterator<true>|
+// | 8 | N | Y | Y | Y | Y | N |sentinel<false>|sentinel<true>|
+// | 9 | N | Y | Y | Y | N | Y |sentinel<false>|sentinel<true>|
+// | 10 | N | Y | Y | N | Y | Y |sentinel<false>| - |
+// | 11 | N | Y | N | Y | Y | Y |sentinel<false>|sentinel<true>|
+// | 12 | N | N | Y | Y | Y | Y |sentinel<false>|sentinel<true>|
+//
+//
+
+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<ForwardCommonInner> 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<decltype(jv.end()), decltype(jv.begin())>);
+ static_assert(HasConstEnd<decltype(jv)>);
+ static_assert(std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(std::ranges::common_range<decltype(jv)>);
+ static_assert(std::ranges::common_range<const decltype(jv)>);
}
- // Non const not common, input 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 2
+ ForwardNonCommonInner inners[3] = {buffer[0], buffer[1], buffer[2]};
+ SimpleForwardCommonOuter<ForwardNonCommonInner> 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<decltype(jv)>);
+ static_assert(std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ // test ID 3
+ InputCommonInner inners[3] = {buffer[0], buffer[1], buffer[2]};
+ SimpleForwardCommonOuter<InputCommonInner> 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<decltype(jv)>);
+ static_assert(std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ // test ID 4
+ ForwardCommonInner inners[2] = {buffer[0], buffer[1]};
+ InnerRValue<SimpleForwardCommonOuter<ForwardCommonInner>> outer{inners};
+
+ std::ranges::join_view jv(outer);
+ assert(jv.end() == std::ranges::next(jv.begin(), 8));
+
+ static_assert(!HasConstEnd<decltype(jv)>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ // test ID 5
+ ForwardCommonInner inners[4] = {buffer[0], buffer[1], buffer[2], buffer[3]};
+ SimpleForwardNonCommonOuter<ForwardCommonInner> 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<decltype(jv.end()), decltype(jv.begin())>);
+ static_assert(HasConstEnd<decltype(jv)>);
+ static_assert(std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
}
- // 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<ForwardCommonInner> 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<decltype(jv.end()), decltype(jv.begin())>);
+ static_assert(HasConstEnd<decltype(jv)>);
+ static_assert(std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
}
- // Const not common, input range.
{
- static_assert(std::is_reference_v<std::ranges::range_reference_t<const CopyableParent>>);
+ // test ID 7
+ ForwardCommonInner inners[1] = {buffer[0]};
+ NonSimpleForwardCommonOuter<ForwardCommonInner> 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(), 4));
+ assert(std::as_const(jv).end() == std::ranges::next(std::as_const(jv).begin(), 4));
+
+ static_assert(HasConstEnd<decltype(jv)>);
+ static_assert(!std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(std::ranges::common_range<decltype(jv)>);
+ static_assert(std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ // test ID 8
+ ForwardNonCommonInner inners[3] = {buffer[0], buffer[1], buffer[2]};
+ NonSimpleForwardCommonOuter<ForwardNonCommonInner> 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<decltype(jv)>);
+ static_assert(!std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ // test ID 9
+ InputCommonInner inners[3] = {buffer[0], buffer[1], buffer[2]};
+ NonSimpleForwardCommonOuter<InputCommonInner> 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<decltype(jv)>);
+ static_assert(!std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ // test ID 10
+ ForwardCommonInner inners[2] = {buffer[0], buffer[1]};
+ InnerRValue<NonSimpleForwardCommonOuter<ForwardCommonInner>> outer{inners};
+
+ std::ranges::join_view jv(outer);
+ assert(jv.end() == std::ranges::next(jv.begin(), 8));
+
+ static_assert(!HasConstEnd<decltype(jv)>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ // test ID 11
+ ForwardCommonInner inners[4] = {buffer[0], buffer[1], buffer[2], buffer[3]};
+ NonSimpleForwardNonCommonOuter<ForwardCommonInner> 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<decltype(jv.end()), decltype(jv.begin())>);
+ static_assert(HasConstEnd<decltype(jv)>);
+ static_assert(!std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ // test ID 12
+ ForwardCommonInner inners[4] = {buffer[0], buffer[1], buffer[2], buffer[3]};
+ NonSimpleInputCommonOuter<ForwardCommonInner> 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<decltype(jv)>);
+ static_assert(!std::same_as<decltype(jv.end()), decltype(std::as_const(jv).end())>);
+ static_assert(!std::ranges::common_range<decltype(jv)>);
+ static_assert(!std::ranges::common_range<const decltype(jv)>);
+ }
+
+ {
+ std::ranges::join_view jv(ConstNotRange{});
+ static_assert(!HasConstEnd<decltype(jv)>);
}
// Has some empty children.
@@ -65,42 +238,49 @@ constexpr bool test() {
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 5));
}
+
// Parent is empty.
{
CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
std::ranges::join_view jv(ParentView(children, 0));
assert(jv.end() == jv.begin());
}
+
// Parent size is one.
{
CopyableChild children[1] = {CopyableChild(buffer[0])};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.end() == std::ranges::next(jv.begin(), 4));
}
+
// Parent and child size is one.
{
CopyableChild children[1] = {CopyableChild(buffer[0], 1)};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.end() == std::ranges::next(jv.begin()));
}
+
// Parent size is one child is empty
{
CopyableChild children[1] = {CopyableChild(buffer[0], 0)};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.end() == jv.begin());
}
+
// Has all empty children.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 0), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == jv.begin());
}
+
// First child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 4));
}
+
// Last child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 4), CopyableChild(buffer[2], 4), 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
index c9c60b7f8d558..030cc62632285 100644
--- 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 <string>
#include <vector>
-#include "test_macros.h"
#include "types.h"
-
-template<class R, class I>
-bool isEqual(R &r, I i) {
+template <class R, class I>
+bool isEqual(R& r, I i) {
for (auto e : r)
if (e != *i++)
return false;
@@ -32,7 +30,7 @@ bool isEqual(R &r, I i) {
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<int*>(buffer);
+ int* flattened = reinterpret_cast<int*>(buffer);
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView(children));
@@ -46,5 +44,22 @@ int main(int, char**) {
assert(isEqual(jv, check.begin()));
}
+ {
+ // P2328R1 join_view should join all views of ranges
+ // join a range of prvalue containers
+ std::vector x{1, 2, 3, 4};
+ auto y = x | std::views::transform([](auto i) {
+ std::vector<int> v(i);
+ for (int& ii : v) {
+ ii = i;
+ }
+ return v;
+ });
+
+ std::ranges::join_view jv(y);
+ std::vector<int> 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
index ac9624f6f90aa..d37bd91a74898 100644
--- 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,29 +15,147 @@
#include <cassert>
#include <ranges>
-#include "test_macros.h"
#include "../types.h"
+template <class T>
+concept HasArrow = std::input_iterator<T> && (std::is_pointer_v<T> || requires(T i) { i.operator->(); });
+
+template <class Base>
+struct move_only_input_iter_with_arrow {
+ Base it_;
+
+ using value_type = std::iter_value_t<Base>;
+ using
diff erence_type = std::intptr_t;
+ using iterator_concept = std::input_iterator_tag;
+
+ constexpr move_only_input_iter_with_arrow(Base it) : it_(std::move(it)) {}
+ constexpr move_only_input_iter_with_arrow(move_only_input_iter_with_arrow&&) = default;
+ constexpr move_only_input_iter_with_arrow(const move_only_input_iter_with_arrow&) = delete;
+ constexpr move_only_input_iter_with_arrow& operator=(move_only_input_iter_with_arrow&&) = default;
+ constexpr move_only_input_iter_with_arrow& operator=(const move_only_input_iter_with_arrow&) = delete;
+
+ constexpr move_only_input_iter_with_arrow& operator++() {
+ ++it_;
+ return *this;
+ }
+ constexpr void operator++(int) { ++it_; }
+
+ constexpr std::iter_reference_t<Base> operator*() const { return *it_; }
+ constexpr auto operator->() const
+ requires(HasArrow<Base> && std::copyable<Base>) {
+ return it_;
+ }
+};
+static_assert(!std::copyable<move_only_input_iter_with_arrow<int*>>);
+static_assert(std::input_iterator<move_only_input_iter_with_arrow<int*>>);
+
+template <class Base>
+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_with_arrow<Base>& other) const { return it_ == other.it_; }
+};
+static_assert(std::sentinel_for<move_iter_sentinel<int*>, move_only_input_iter_with_arrow<int*>>);
+
+struct MoveOnlyIterInner : BufferView<move_only_input_iter_with_arrow<Box*>, move_iter_sentinel<Box*>> {
+ using BufferView::BufferView;
+
+ using iterator = move_only_input_iter_with_arrow<Box*>;
+ using sentinel = move_iter_sentinel<Box*>;
+
+ iterator begin() const { return data_; }
+ sentinel end() const { return sentinel{data_ + size_}; }
+};
+static_assert(std::ranges::input_range<MoveOnlyIterInner>);
+
+template <class Base>
+struct arrow_input_iter {
+ Base it_;
+
+ using value_type = std::iter_value_t<Base>;
+ using
diff erence_type = std::intptr_t;
+ using iterator_concept = std::input_iterator_tag;
+
+ arrow_input_iter() = 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<Base> operator*() const { return *it_; }
+ constexpr auto operator->() const { return it_; }
+
+ friend constexpr bool operator==(const arrow_input_iter& x, const arrow_input_iter& y) = default;
+};
+
+using ArrowInner = BufferView<arrow_input_iter<Box*>>;
+static_assert(std::ranges::input_range<ArrowInner>);
+static_assert(HasArrow<std::ranges::iterator_t<ArrowInner>>);
+
constexpr bool test() {
- Box buffer[4][4] = {{{1111}, {2222}, {3333}, {4444}}, {{555}, {666}, {777}, {888}}, {{99}, {1010}, {1111}, {1212}}, {{13}, {14}, {15}, {16}}};
+ Box buffer[4][4] = {{{1111}, {2222}, {3333}, {4444}},
+ {{555}, {666}, {777}, {888}},
+ {{99}, {1010}, {1111}, {1212}},
+ {{13}, {14}, {15}, {16}}};
{
// Copyable input iterator with arrow.
- ValueView<Box> children[4] = {ValueView(buffer[0]), ValueView(buffer[1]), ValueView(buffer[2]), ValueView(buffer[3])};
+ ValueView<Box> children[4] = {ValueView(buffer[0]), ValueView(buffer[1]), ValueView(buffer[2]),
+ ValueView(buffer[3])};
std::ranges::join_view jv(ValueView<ValueView<Box>>{children});
assert(jv.begin()->x == 1111);
+ static_assert(HasArrow<decltype(jv.begin())>);
}
{
std::ranges::join_view jv(buffer);
assert(jv.begin()->x == 1111);
+ static_assert(HasArrow<decltype(jv.begin())>);
}
{
const std::ranges::join_view jv(buffer);
assert(jv.begin()->x == 1111);
+ static_assert(HasArrow<decltype(jv.begin())>);
+ }
+
+ {
+ // LWG3500 `join_view::iterator::operator->()` is bogus
+ // `operator->` should not be defined if inner iterator is not copyable
+ // has-arrow<InnerIter> && !copyable<InnerIter>
+ static_assert(HasArrow<move_only_input_iter_with_arrow<int*>>);
+ MoveOnlyIterInner inners[2] = {buffer[0], buffer[1]};
+ std::ranges::join_view jv{inners};
+ static_assert(HasArrow<decltype(std::ranges::begin(inners[0]))>);
+ static_assert(!HasArrow<decltype(jv.begin())>);
+ }
+
+ {
+ // LWG3500 `join_view::iterator::operator->()` is bogus
+ // `operator->` should not be defined if inner iterator does not have `operator->`
+ // !has-arrow<InnerIter> && copyable<InnerIter>
+ using Inner = BufferView<forward_iterator<Box*>>;
+ Inner inners[2] = {buffer[0], buffer[1]};
+ std::ranges::join_view jv{inners};
+ static_assert(!HasArrow<decltype(std::ranges::begin(inners[0]))>);
+ static_assert(!HasArrow<decltype(jv.begin())>);
}
+ {
+ // arrow returns inner iterator
+ ArrowInner inners[2] = {buffer[0], buffer[1]};
+ std::ranges::join_view jv{inners};
+ static_assert(HasArrow<decltype(std::ranges::begin(inners[0]))>);
+ static_assert(HasArrow<decltype(jv.begin())>);
+
+ auto jv_it = jv.begin();
+ std::same_as<arrow_input_iter<Box*>> 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
index dba4dd403a88a..28632086d01c8 100644
--- 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<!Const> i);
+// constexpr iterator(iterator<!Const> i)
+// requires Const &&
+// convertible_to<iterator_t<V>, OuterIter> &&
+// convertible_to<iterator_t<InnerRng>, InnerIter>;
#include <cassert>
#include <ranges>
@@ -17,17 +20,60 @@
#include "test_macros.h"
#include "../types.h"
+using ConstCompatibleInner = BufferView<int*>;
+
+using ConstIncompatibleInner = BufferView<forward_iterator<const int*>, forward_iterator<const int*>,
+ bidirectional_iterator<int*>, bidirectional_iterator<int*>>;
+
+template <class Inner>
+using ConstCompatibleOuter = BufferView<const Inner*, const Inner*, Inner*, Inner*>;
+
+template <class Inner>
+using ConstIncompatibleOuter = BufferView<forward_iterator<const Inner*>, forward_iterator<const Inner*>,
+ bidirectional_iterator<Inner*>, bidirectional_iterator<Inner*>>;
+
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<iterator, const_iterator>);
+ const_iterator iter2 = iter1;
+ assert(iter1 == iter2);
+
+ // We cannot create a non-const iterator from a const iterator.
+ static_assert(!std::constructible_from<iterator, const_iterator>);
+ }
+
+ // !convertible_to<inner_iterator, inner_const_iterator>>;
+ {
+ ConstIncompatibleInner inners[2] = {buffer[0], buffer[1]};
+ ConstCompatibleOuter<ConstIncompatibleInner> 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<iterator, const_iterator>);
+
+ static_assert(!std::constructible_from<const_iterator, iterator>);
+ static_assert(!std::constructible_from<iterator, const_iterator>);
+ }
- 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<const decltype(jv)> iter2 = iter1;
- assert(iter1 == iter2);
+ // !convertible_to<outer_iterator, outer_const_iterator>>;
+ {
+ ConstCompatibleInner inners[2] = {buffer[0], buffer[1]};
+ ConstIncompatibleOuter<ConstCompatibleInner> 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<iterator, const_iterator>);
- // We cannot create a non-const iterator from a const iterator.
- static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
+ static_assert(!std::constructible_from<const_iterator, iterator>);
+ static_assert(!std::constructible_from<iterator, const_iterator>);
+ }
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
index 2973a3d1907bb..ced15797cf1aa 100644
--- 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,37 @@
#include <cassert>
#include <ranges>
-#include "test_macros.h"
#include "../types.h"
+using NonDefaultCtrIter = cpp20_input_iterator<int*>;
+static_assert(!std::default_initializable<NonDefaultCtrIter>);
+
+using NonDefaultCtrIterView = BufferView<NonDefaultCtrIter, sentinel_wrapper<NonDefaultCtrIter>>;
+static_assert(std::ranges::input_range<NonDefaultCtrIterView>);
+
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<decltype(jv)> 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<decltype(jv)> iter(jv, std::ranges::begin(parent));
+ assert(*iter == 1);
+ }
+
+ {
+ // LWG3569 Inner iterator not default_initializable
+ // With the current spec, the constructor under test invokes Inner iterator's default constructor
+ // even if it is not default constructible
+ // This test is checking that this constructor can be invoked with an inner range with non default
+ // constructible iterator
+ NonDefaultCtrIterView inners[] = {buffer[0], buffer[1]};
+ auto outer = std::views::all(inners);
+ std::ranges::join_view jv(outer);
+ std::ranges::iterator_t<decltype(jv)> 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
index b15363a4d28fe..ed171327b693c 100644
--- 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,32 @@
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator& operator--();
+// requires ref-is-glvalue && bidirectional_range<Base> &&
+// bidirectional_range<range_reference_t<Base>> &&
+// common_range<range_reference_t<Base>>;
// constexpr iterator operator--(int);
+// requires ref-is-glvalue && bidirectional_range<Base> &&
+// bidirectional_range<range_reference_t<Base>> &&
+// common_range<range_reference_t<Base>>;
#include <cassert>
#include <ranges>
+#include <type_traits>
-#include "test_macros.h"
#include "../types.h"
+template <class T>
+concept CanPreDecrement = requires(T& t) { --t; };
+
+template <class T>
+concept CanPostDecrement = requires(T& t) { t--; };
+
+constexpr void noDecrementTest(auto&& jv) {
+ auto iter = jv.begin();
+ static_assert(!CanPreDecrement<decltype(iter)>);
+ static_assert(!CanPostDecrement<decltype(iter)>);
+}
+
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
@@ -29,6 +47,7 @@ constexpr bool test() {
assert(*--iter == i);
}
}
+
{
// outer == ranges::end
std::ranges::join_view jv(buffer);
@@ -37,6 +56,7 @@ constexpr bool test() {
assert(*--iter == i);
}
}
+
{
// outer != ranges::end
std::ranges::join_view jv(buffer);
@@ -45,6 +65,7 @@ constexpr bool test() {
assert(*--iter == i);
}
}
+
{
// outer != ranges::end
std::ranges::join_view jv(buffer);
@@ -53,6 +74,7 @@ constexpr bool test() {
assert(*--iter == i);
}
}
+
{
int small[2][1] = {{1}, {2}};
std::ranges::join_view jv(small);
@@ -62,6 +84,72 @@ constexpr bool test() {
}
}
+ {
+#if defined(__GNUG__) && !defined(__clang__)
+ // This seems to be a gcc bug where evaluating the following code
+ // at compile time results in wrong array index
+ 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);
+
+ decltype(auto) iter2 = --iter1;
+ static_assert(std::same_as<decltype(iter2), iterator&>);
+ assert(&iter1 == &iter2);
+
+ std::same_as<iterator> decltype(auto) iter3 = iter1--;
+ assert(iter3 == std::next(iter1));
+ }
+
+ {
+ // !ref-is-glvalue
+ BidiCommonInner inners[2] = {buffer[0], buffer[1]};
+ InnerRValue<BidiCommonOuter<BidiCommonInner>> outer{inners};
+ std::ranges::join_view jv(outer);
+ noDecrementTest(jv);
+ }
+
+ {
+ // !bidirectional_range<Base>
+ BidiCommonInner inners[2] = {buffer[0], buffer[1]};
+ SimpleForwardCommonOuter<BidiCommonInner> outer{inners};
+ std::ranges::join_view jv(outer);
+ noDecrementTest(jv);
+ }
+
+ {
+ // !bidirectional_range<range_reference_t<Base>>
+ ForwardCommonInner inners[2] = {buffer[0], buffer[1]};
+ std::ranges::join_view jv(inners);
+ noDecrementTest(jv);
+ }
+
+ {
+ // LWG3313 `join_view::iterator::operator--` is incorrectly constrained
+ // `join_view::iterator` should not have `operator--` if
+ // !common_range<range_reference_t<Base>>
+ BidiNonCommonInner inners[2] = {buffer[0], buffer[1]};
+ std::ranges::join_view jv(inners);
+ auto iter = jv.begin();
+ static_assert(!CanPreDecrement<decltype(iter)>);
+ static_assert(!CanPostDecrement<decltype(iter)>);
+ }
+
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
index e8598d2fd228a..204a0568e1286 100644
--- 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,61 @@
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr bool operator==(const iterator& x, const iterator& y);
+// requires ref-is-glvalue && equality_comparable<iterator_t<Base>> &&
+// equality_comparable<iterator_t<range_reference_t<Base>>>;
#include <cassert>
#include <ranges>
-#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<BidiCommonOuter<BidiCommonInner>> outer{inners};
+ std::ranges::join_view jv(outer);
+ auto iter = jv.begin();
+ static_assert(!std::equality_comparable<decltype(iter)>);
+ }
+
+ {
+ // !equality_comparable<iterator_t<Base>>
+ using Inner = BufferView<int*>;
+ using Outer = BufferView<cpp20_input_iterator<Inner*>, sentinel_wrapper<cpp20_input_iterator<Inner*>>>;
+ static_assert(!std::equality_comparable<std::ranges::iterator_t<Outer>>);
+ Inner inners[2] = {buffer[0], buffer[1]};
+ std::ranges::join_view jv(Outer{inners});
+ auto iter = jv.begin();
+ static_assert(!std::equality_comparable<decltype(iter)>);
+ auto const_iter = std::as_const(jv).begin();
+ static_assert(!std::equality_comparable<decltype(const_iter)>);
+ }
+
+ {
+ // !equality_comparable<iterator_t<range_reference_t<Base>>>;
+ using Inner = BufferView<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>;
+ Inner inners[1] = {buffer[0]};
+ std::ranges::join_view jv{inners};
+ auto iter = jv.begin();
+ static_assert(!std::equality_comparable<decltype(iter)>);
+ auto const_iter = std::as_const(jv).begin();
+ static_assert(!std::equality_comparable<decltype(const_iter)>);
+ }
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
index 8c3f6fc7f9861..8478db511d8dc 100644
--- 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<Base> &&
+// forward_range<range_reference_t<Base>>;
#include <cassert>
#include <ranges>
@@ -34,6 +36,7 @@ constexpr bool test() {
assert(*iter++ == i);
}
}
+
{
ValueView<int> children[4] = {ValueView(buffer1[0]), ValueView(buffer1[1]), ValueView(buffer2[0]), ValueView(buffer2[1])};
std::ranges::join_view jv(ValueView<ValueView<int>>{children});
@@ -45,12 +48,14 @@ constexpr bool test() {
ASSERT_SAME_TYPE(decltype(iter++), void);
}
+
{
std::ranges::join_view jv(buffer1);
auto iter = std::next(jv.begin(), 7);
assert(*iter++ == 8);
assert(iter == jv.end());
}
+
{
int small[2][1] = {{1}, {2}};
std::ranges::join_view jv(small);
@@ -59,6 +64,7 @@ constexpr bool test() {
assert(*iter++ == i);
}
}
+
// Has some empty children.
{
CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 1), CopyableChild(buffer2[1], 0)};
@@ -71,12 +77,14 @@ constexpr bool test() {
assert(*iter == 9); iter++;
assert(iter == jv.end());
}
+
// Parent is empty.
{
CopyableChild children[4] = {CopyableChild(buffer1[0]), CopyableChild(buffer1[1]), CopyableChild(buffer2[0]), CopyableChild(buffer2[1])};
std::ranges::join_view jv(ParentView(children, 0));
assert(jv.begin() == jv.end());
}
+
// Parent size is one.
{
CopyableChild children[1] = {CopyableChild(buffer1[0])};
@@ -88,6 +96,7 @@ constexpr bool test() {
assert(*iter == 4); iter++;
assert(iter == jv.end());
}
+
// Parent and child size is one.
{
CopyableChild children[1] = {CopyableChild(buffer1[0], 1)};
@@ -96,18 +105,21 @@ constexpr bool test() {
assert(*iter == 1); iter++;
assert(iter == jv.end());
}
+
// Parent size is one child is empty
{
CopyableChild children[1] = {CopyableChild(buffer1[0], 0)};
std::ranges::join_view jv(ParentView(children, 1));
assert(jv.begin() == jv.end());
}
+
// Has all empty children.
{
CopyableChild children[4] = {CopyableChild(buffer1[0], 0), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 0), CopyableChild(buffer2[1], 0)};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.begin() == jv.end());
}
+
// First child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 0), CopyableChild(buffer2[1], 0)};
@@ -119,6 +131,7 @@ constexpr bool test() {
assert(*iter == 4); iter++;
assert(iter == jv.end());
}
+
// Last child is empty, others are not.
{
CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 4), CopyableChild(buffer2[0], 4), CopyableChild(buffer2[1], 0)};
@@ -129,6 +142,7 @@ constexpr bool test() {
iter++;
}
}
+
// operator++();
{
std::ranges::join_view jv(buffer1);
@@ -137,6 +151,7 @@ constexpr bool test() {
assert(*++iter == i);
}
}
+
{
ValueView<int> children[4] = {ValueView(buffer1[0]), ValueView(buffer1[1]), ValueView(buffer2[0]), ValueView(buffer2[1])};
std::ranges::join_view jv(ValueView<ValueView<int>>{children});
@@ -148,6 +163,46 @@ constexpr bool test() {
ASSERT_SAME_TYPE(decltype(++iter), decltype(iter)&);
}
+ {
+ // check return value
+ std::ranges::join_view jv(buffer1);
+ auto iter = jv.begin();
+ using iterator = decltype(iter);
+
+ decltype(auto) iter2 = ++iter;
+ static_assert(std::is_same_v<decltype(iter2), iterator&>);
+ assert(&iter2 == &iter);
+
+ std::same_as<iterator> decltype(auto) iter3 = iter++;
+ assert(std::next(iter3) == iter);
+ }
+
+ {
+ // !ref-is-glvalue
+ BidiCommonInner inners[2] = {buffer1[0], buffer1[1]};
+ InnerRValue<BidiCommonOuter<BidiCommonInner>> outer{inners};
+ std::ranges::join_view jv(outer);
+ auto iter = jv.begin();
+ static_assert(std::is_void_v<decltype(iter++)>);
+ }
+
+ {
+ // !forward_range<Base>
+ BufferView<int*> inners[2] = {buffer1[0], buffer1[1]};
+ using Outer = SimpleInputCommonOuter<BufferView<int*>>;
+ std::ranges::join_view jv{Outer(inners)};
+ auto iter = jv.begin();
+ static_assert(std::is_void_v<decltype(iter++)>);
+ }
+
+ {
+ // !forward_range<range_reference_t<Base>>
+ InputCommonInner inners[1] = {buffer1[0]};
+ std::ranges::join_view jv{inners};
+ auto iter = jv.begin();
+ static_assert(std::is_void_v<decltype(iter++)>);
+ }
+
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
index a66b87a71312f..01a3105ab6452 100644
--- 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,43 @@
#include <cassert>
#include <ranges>
-#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<decltype(jv.begin())>())));
-
+ {
+ std::ranges::join_view jv(buffer);
+ assert(std::ranges::iter_move(jv.begin()) == 1);
+ static_assert(std::is_same_v<decltype(std::ranges::iter_move(jv.begin())), int&&>);
+
+ static_assert(noexcept(std::ranges::iter_move(std::declval<decltype(jv.begin())>())));
+ }
+
+ {
+ // iter_move calls inner's iter_move and calls
+ // iter_move on the correct inner iterator
+ 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<std::pair<int&&, int&&>> 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<std::pair<int&&, int&&>> 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
index df6bc69cb17fc..d286bd829b933 100644
--- 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,56 @@
#include <cassert>
#include <ranges>
-#include "test_macros.h"
#include "../types.h"
+using NonSwappableView = BufferView<copying_iterator<int*>>;
+static_assert(std::ranges::input_range<NonSwappableView>);
+static_assert(!std::indirectly_swappable<std::ranges::iterator_t<NonSwappableView>>);
+
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);
+ {
+ 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);
- static_assert(noexcept(std::ranges::iter_swap(iter1, iter2)));
+ assert(buffer[0][0] == 1);
+ assert(buffer[0][1] == 2);
+ assert(iter_swap_called_times == 1);
+ }
+ {
+ // LWG3517 `join_view::iterator`'s `iter_swap` is underconstrained
+ // `iter_swap` should not be defined if Inner's iterator does not indirectly_swappable
+ NonSwappableView inners[2] = {buffer[0], buffer[1]};
+ std::ranges::join_view jv(inners);
+ using Iter = std::ranges::iterator_t<decltype(jv)>;
+ static_assert(!std::indirectly_swappable<Iter>);
+ }
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
index 370fa026b086b..102999d567903 100644
--- 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 @@ struct InputView : std::ranges::view_base {
sentinel_wrapper<cpp17_input_iterator<T*>> end() const;
};
+template <class T, class V>
+struct
diff _type_iter {
+ using iterator_category = std::input_iterator_tag;
+ using value_type = V;
+ using
diff erence_type = T;
+
+ V& operator*() const;
+
diff _type_iter& operator++();
+ void operator++(int);
+ friend constexpr bool operator==(
diff _type_iter,
diff _type_iter) = default;
+};
+
+template <class T, class V = int>
+struct DiffTypeRange : std::ranges::view_base {
+
diff _type_iter<T, V> begin() const;
+
diff _type_iter<T, V> end() const;
+};
+
template<class T>
concept HasIterCategory = requires { typename T::iterator_category; };
@@ -38,25 +56,70 @@ void test() {
std::ranges::join_view jv(buffer);
using Iter = std::ranges::iterator_t<decltype(jv)>;
- ASSERT_SAME_TYPE(Iter::iterator_concept, std::bidirectional_iterator_tag);
- ASSERT_SAME_TYPE(Iter::iterator_category, std::bidirectional_iterator_tag);
- ASSERT_SAME_TYPE(Iter::
diff erence_type, std::ptr
diff _t);
- ASSERT_SAME_TYPE(Iter::value_type, int);
+ static_assert(std::is_same_v<Iter::iterator_concept, std::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<Iter::iterator_category, std::bidirectional_iterator_tag>);
+ static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+ static_assert(std::is_same_v<Iter::value_type, int>);
+ static_assert(HasIterCategory<Iter>);
}
+
{
using Iter = std::ranges::iterator_t<std::ranges::join_view<ForwardView<ForwardView<int>>>>;
- ASSERT_SAME_TYPE(Iter::iterator_concept, std::forward_iterator_tag);
- ASSERT_SAME_TYPE(Iter::iterator_category, std::forward_iterator_tag);
- ASSERT_SAME_TYPE(Iter::
diff erence_type, std::ptr
diff _t);
- ASSERT_SAME_TYPE(Iter::value_type, int);
+ static_assert(std::is_same_v<Iter::iterator_concept, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<Iter::iterator_category, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+ static_assert(std::is_same_v<Iter::value_type, int>);
+ static_assert(HasIterCategory<Iter>);
}
+
{
using Iter = std::ranges::iterator_t<std::ranges::join_view<InputView<InputView<int>>>>;
- ASSERT_SAME_TYPE(Iter::iterator_concept, std::input_iterator_tag);
+ static_assert(std::is_same_v<Iter::iterator_concept, std::input_iterator_tag>);
static_assert(!HasIterCategory<Iter>);
- ASSERT_SAME_TYPE(Iter::
diff erence_type, std::ptr
diff _t);
- ASSERT_SAME_TYPE(Iter::value_type, int);
+ static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+ static_assert(std::is_same_v<Iter::value_type, int>);
+ }
+
+ {
+ // LWG3535 `join_view::iterator::iterator_category` and `::iterator_concept` lie
+ // Bidi non common inner range should not have bidirectional_iterator_tag
+ using Base = BidiCommonOuter<BidiNonCommonInner>;
+ using Iter = std::ranges::iterator_t<std::ranges::join_view<Base>>;
+ static_assert(std::is_same_v<Iter::iterator_concept, std::forward_iterator_tag>);
+ static_assert(std::is_same_v<Iter::iterator_category, std::forward_iterator_tag>);
+ static_assert(HasIterCategory<Iter>);
+ static_assert(std::is_same_v<Iter::
diff erence_type, std::ptr
diff _t>);
+ static_assert(std::is_same_v<Iter::value_type, int>);
+ }
+
+ {
+ // !ref-is-glvalue
+ using Outer = InnerRValue<BidiCommonOuter<BidiCommonInner>>;
+ using Iter = std::ranges::iterator_t<std::ranges::join_view<Outer>>;
+ static_assert(!HasIterCategory<Iter>);
+ static_assert(std::is_same_v<Iter::iterator_concept, std::input_iterator_tag>);
+ }
+
+ {
+ // value_type == inner's value_type
+ using Inner = IterMoveSwapAwareView;
+ using InnerValue = std::ranges::range_value_t<Inner>;
+ using InnerReference = std::ranges::range_reference_t<Inner>;
+ static_assert(!std::is_same_v<InnerValue, std::remove_cvref<InnerReference>>);
+
+ using Outer = BidiCommonOuter<Inner>;
+ using Iter = std::ranges::iterator_t<std::ranges::join_view<Outer>>;
+ static_assert(std::is_same_v<InnerValue, std::pair<int, int>>);
+ static_assert(std::is_same_v<Iter::value_type, std::pair<int, int>>);
+ }
+
+ {
+ //
diff erence_type
+ using Inner = DiffTypeRange<std::intptr_t>;
+ using Outer = DiffTypeRange<std::ptr
diff _t, Inner>;
+ using Iter = std::ranges::iterator_t<std::ranges::join_view<Outer>>;
+ static_assert(std::is_same_v<Iter::
diff erence_type, std::common_type_t<std::intptr_t, std::ptr
diff _t>>);
}
}
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
index 806b45fae6474..ffb2e7a6f7b57 100644
--- 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,72 @@
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr sentinel(sentinel<!Const> s);
+// requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
#include <cassert>
#include <ranges>
-#include "test_macros.h"
#include "../types.h"
+template <class T>
+struct convertible_sentinel_wrapper {
+ explicit convertible_sentinel_wrapper() = default;
+ constexpr convertible_sentinel_wrapper(const T& it) : it_(it) {}
+
+ template <class U>
+ requires std::convertible_to<const U&, T>
+ constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper<U>& other) : it_(other.it_) {}
+
+ constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) {
+ return self.it_ == other;
+ }
+ T it_;
+};
+
+struct ConstConveritbleView : BufferView<BufferView<int*>*> {
+ using BufferView<BufferView<int*>*>::BufferView;
+
+ using sentinel = convertible_sentinel_wrapper<BufferView<int*>*>;
+ using const_sentinel = convertible_sentinel_wrapper<const BufferView<int*>*>;
+
+ constexpr BufferView<int*>* begin() { return data_; }
+ constexpr const BufferView<int*>* begin() const { return data_; }
+ constexpr sentinel end() { return sentinel(data_ + size_); }
+ constexpr const_sentinel end() const { return const_sentinel(data_ + size_); }
+};
+static_assert(!std::ranges::common_range<ConstConveritbleView>);
+static_assert(std::convertible_to<std::ranges::sentinel_t<ConstConveritbleView>,
+ std::ranges::sentinel_t<ConstConveritbleView const>>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<ConstConveritbleView>);
+
constexpr bool test() {
int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}};
+ {
+ BufferView<int*> inners[] = {buffer[0], buffer[1], buffer[2]};
+ ConstConveritbleView outer(inners);
+ std::ranges::join_view jv(outer);
+ auto sent1 = jv.end();
+ std::ranges::sentinel_t<const decltype(jv)> sent2 = sent1;
+ assert(std::as_const(jv).begin() != sent2);
+ assert(std::ranges::next(std::as_const(jv).begin(), 12) == sent2);
- 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<const decltype(jv)> 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<decltype(sent1), decltype(sent2)>);
+ // We cannot create a non-const sentinel from a const sentinel.
+ static_assert(!std::constructible_from<decltype(sent1), decltype(sent2)>);
+ }
+ {
+ // cannot create a const sentinel from a non-const sentinel if the underlying
+ // const sentinel cannot be created from the underlying non-const sentinel
+ using Inner = BufferView<int*>;
+ using ConstInconvertibleOuter =
+ BufferView<forward_iterator<const Inner*>, sentinel_wrapper<forward_iterator<const Inner*>>,
+ bidirectional_iterator<Inner*>, sentinel_wrapper<bidirectional_iterator<Inner*>>>;
+ using JoinView = std::ranges::join_view<ConstInconvertibleOuter>;
+ using sentinel = std::ranges::sentinel_t<JoinView>;
+ using const_sentinel = std::ranges::sentinel_t<const JoinView>;
+ static_assert(!std::constructible_from<sentinel, const_sentinel>);
+ static_assert(!std::constructible_from<const_sentinel, sentinel>);
+ }
return true;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp
index 5ba90be166e56..7f1ce0b45250c 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp
@@ -14,11 +14,49 @@
// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
#include <cassert>
+#include <concepts>
#include <ranges>
-#include "test_macros.h"
#include "../types.h"
+template <class Iter, class Sent>
+concept EqualityComparable = std::invocable<std::equal_to<>, const Iter&, const Sent&> ;
+
+using Iterator = random_access_iterator<BufferView<int*>*>;
+using ConstIterator = random_access_iterator<const BufferView<int*>*>;
+
+template <bool Const>
+struct ConstComparableSentinel {
+
+ using Iter = std::conditional_t<Const, ConstIterator, Iterator>;
+ Iter iter_;
+
+ explicit ConstComparableSentinel() = default;
+ constexpr explicit ConstComparableSentinel(const Iter& it) : iter_(it) {}
+
+ constexpr friend bool operator==(const Iterator& i, const ConstComparableSentinel& s) {
+ return base(i) == base(s.iter_);
+ }
+
+ constexpr friend bool operator==(const ConstIterator& i, const ConstComparableSentinel& s) {
+ return base(i) == base(s.iter_);
+ }
+};
+
+struct ConstComparableView : BufferView<BufferView<int*>*> {
+ using BufferView<BufferView<int*>*>::BufferView;
+
+ constexpr auto begin() { return Iterator(data_); }
+ constexpr auto begin() const { return ConstIterator(data_); }
+ constexpr auto end() { return ConstComparableSentinel<false>(Iterator(data_ + size_)); }
+ constexpr auto end() const { return ConstComparableSentinel<true>(ConstIterator(data_ + size_)); }
+};
+
+static_assert(EqualityComparable<std::ranges::iterator_t<ConstComparableView>,
+ std::ranges::sentinel_t<const ConstComparableView>>);
+static_assert(EqualityComparable<std::ranges::iterator_t<const ConstComparableView>,
+ std::ranges::sentinel_t<ConstComparableView>>);
+
constexpr bool test() {
int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
@@ -26,18 +64,25 @@ constexpr bool test() {
ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])};
auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 16));
+ static_assert(!EqualityComparable<decltype(std::as_const(jv).begin()), decltype(jv.end())>);
+ static_assert(!EqualityComparable<decltype(jv.begin()), decltype(std::as_const(jv).end())>);
}
+
{
- CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
+ CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]),
+ CopyableChild(buffer[3])};
const auto jv = std::ranges::join_view(ParentView(children));
assert(jv.end() == std::ranges::next(jv.begin(), 16));
}
+
+ // test iterator<Const> == sentinel<!Const>
{
- CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])};
- const std::ranges::join_view jvc(CopyableParent{children});
- std::ranges::join_view jv(CopyableParent{children});
- assert(jvc.end() == std::ranges::next(jv.begin(), 16));
- assert( jv.end() == std::ranges::next(jvc.begin(), 16));
+ BufferView<int*> inners[] = {buffer[0], buffer[1]};
+ ConstComparableView outer(inners);
+ auto jv = std::ranges::join_view(outer);
+ assert(jv.end() == std::ranges::next(jv.begin(), 8));
+ assert(std::as_const(jv).end() == std::ranges::next(jv.begin(), 8));
+ assert(jv.end() == std::ranges::next(std::as_const(jv).begin(), 8));
}
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
index 7a3dafb4aa1cd..4dd3cf7cfda43 100644
--- 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,120 +10,141 @@
#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H
#include <concepts>
+#include <tuple>
#include "test_macros.h"
#include "test_iterators.h"
-int globalBuffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}};
+inline int globalBuffer[4][4] = {
+ {1111, 2222, 3333, 4444},
+ {555, 666, 777, 888},
+ {99, 1010, 1111, 1212},
+ {13, 14, 15, 16},
+};
struct ChildView : std::ranges::view_base {
- int *ptr_;
+ int* ptr_;
+
+ using iterator = cpp20_input_iterator<int*>;
+ using const_iterator = cpp20_input_iterator<const int*>;
+ using sentinel = sentinel_wrapper<iterator>;
+ using const_sentinel = sentinel_wrapper<const_iterator>;
- constexpr ChildView(int *ptr = globalBuffer[0]) : ptr_(ptr) {}
+ constexpr ChildView(int* ptr = globalBuffer[0]) : ptr_(ptr) {}
ChildView(const ChildView&) = delete;
ChildView(ChildView&&) = default;
ChildView& operator=(const ChildView&) = delete;
ChildView& operator=(ChildView&&) = default;
- constexpr cpp20_input_iterator<int *> begin() { return cpp20_input_iterator<int *>(ptr_); }
- constexpr cpp20_input_iterator<const int *> begin() const { return cpp20_input_iterator<const int *>(ptr_); }
- constexpr int *end() { return ptr_ + 4; }
- constexpr const int *end() const { return ptr_ + 4; }
+ constexpr iterator begin() { return iterator(ptr_); }
+ constexpr const_iterator begin() const { return const_iterator(ptr_); }
+ constexpr sentinel end() { return sentinel(iterator(ptr_ + 4)); }
+ constexpr const_sentinel end() const { return const_sentinel(const_iterator(ptr_ + 4)); }
};
-constexpr bool operator==(const cpp20_input_iterator<int*> &lhs, int* rhs) { return base(lhs) == rhs; }
-constexpr bool operator==(int* lhs, const cpp20_input_iterator<int*> &rhs) { return base(rhs) == lhs; }
-
-ChildView globalChildren[4] = {ChildView(globalBuffer[0]), ChildView(globalBuffer[1]), ChildView(globalBuffer[2]), ChildView(globalBuffer[3])};
+inline ChildView globalChildren[4] = {
+ ChildView(globalBuffer[0]),
+ ChildView(globalBuffer[1]),
+ ChildView(globalBuffer[2]),
+ ChildView(globalBuffer[3]),
+};
-template<class T>
+template <class T>
struct ParentView : std::ranges::view_base {
- T *ptr_;
+ T* ptr_;
unsigned size_;
- constexpr ParentView(T *ptr, unsigned size = 4)
- : ptr_(ptr), size_(size) {}
- constexpr ParentView(ChildView *ptr = globalChildren, unsigned size = 4)
+ using iterator = cpp20_input_iterator<T*>;
+ using const_iterator = cpp20_input_iterator<const T*>;
+ using sentinel = sentinel_wrapper<iterator>;
+ using const_sentinel = sentinel_wrapper<const_iterator>;
+
+ constexpr ParentView(T* ptr, unsigned size = 4) : ptr_(ptr), size_(size) {}
+ constexpr ParentView(ChildView* ptr = globalChildren, unsigned size = 4)
requires std::same_as<ChildView, T>
- : ptr_(ptr), size_(size) {}
+ : ptr_(ptr), size_(size) {}
ParentView(const ParentView&) = delete;
ParentView(ParentView&&) = default;
ParentView& operator=(const ParentView&) = delete;
ParentView& operator=(ParentView&&) = default;
- constexpr cpp20_input_iterator<T *> begin() { return cpp20_input_iterator<T *>(ptr_); }
- constexpr cpp20_input_iterator<const T *> begin() const { return cpp20_input_iterator<const T *>(ptr_); }
- constexpr T *end() { return ptr_ + size_; }
- constexpr const T *end() const { return ptr_ + size_; }
+ constexpr iterator begin() { return iterator(ptr_); }
+ constexpr const_iterator begin() const { return const_iterator(ptr_); }
+ constexpr sentinel end() { return sentinel(iterator(ptr_ + size_)); }
+ constexpr const_sentinel end() const { return const_sentinel(const_iterator(ptr_ + size_)); }
};
-// TODO: remove these bogus operators
-template<class T>
-constexpr bool operator==(const cpp20_input_iterator<T*> &lhs, T *rhs) { return base(lhs) == rhs; }
-template<class T>
-constexpr bool operator==(T *lhs, const cpp20_input_iterator<T*> &rhs) { return base(rhs) == lhs; }
struct CopyableChild : std::ranges::view_base {
- int *ptr_;
+ int* ptr_;
unsigned size_;
- constexpr CopyableChild(int *ptr = globalBuffer[0], unsigned size = 4)
- : ptr_(ptr), size_(size) {}
- constexpr cpp17_input_iterator<int *> begin() { return cpp17_input_iterator<int *>(ptr_); }
- constexpr cpp17_input_iterator<const int *> begin() const { return cpp17_input_iterator<const int *>(ptr_); }
- constexpr int *end() { return ptr_ + size_; }
- constexpr const int *end() const { return ptr_ + size_; }
+ using iterator = cpp17_input_iterator<int*>;
+ using const_iterator = cpp17_input_iterator<const int*>;
+ using sentinel = sentinel_wrapper<iterator>;
+ using const_sentinel = sentinel_wrapper<const_iterator>;
+
+ constexpr CopyableChild(int* ptr = globalBuffer[0], unsigned size = 4) : ptr_(ptr), size_(size) {}
+
+ constexpr iterator begin() { return iterator(ptr_); }
+ constexpr const_iterator begin() const { return const_iterator(ptr_); }
+ constexpr sentinel end() { return sentinel(iterator(ptr_ + size_)); }
+ constexpr const_sentinel end() const { return const_sentinel(const_iterator(ptr_ + size_)); }
};
-// TODO: remove these bogus operators
-constexpr bool operator==(const cpp17_input_iterator<const int*> &lhs, const int* rhs) { return base(lhs) == rhs; }
-constexpr bool operator==(const int* lhs, const cpp17_input_iterator<const int*> &rhs) { return base(rhs) == lhs; }
struct CopyableParent : std::ranges::view_base {
- CopyableChild *ptr_;
- constexpr CopyableParent(CopyableChild *ptr) : ptr_(ptr) {}
+ CopyableChild* ptr_;
+
+ using iterator = cpp17_input_iterator<CopyableChild*>;
+ using const_iterator = cpp17_input_iterator<const CopyableChild*>;
+ using sentinel = sentinel_wrapper<iterator>;
+ using const_sentinel = sentinel_wrapper<const_iterator>;
+
+ constexpr CopyableParent(CopyableChild* ptr) : ptr_(ptr) {}
- constexpr cpp17_input_iterator<CopyableChild *> begin() { return cpp17_input_iterator<CopyableChild *>(ptr_); }
- constexpr cpp17_input_iterator<const CopyableChild *> begin() const { return cpp17_input_iterator<const CopyableChild *>(ptr_); }
- constexpr CopyableChild *end() { return ptr_ + 4; }
- constexpr const CopyableChild *end() const { return ptr_ + 4; }
+ constexpr iterator begin() { return iterator(ptr_); }
+ constexpr const_iterator begin() const { return const_iterator(ptr_); }
+ constexpr sentinel end() { return sentinel(iterator(ptr_ + 4)); }
+ constexpr const_sentinel end() const { return const_sentinel(const_iterator(ptr_ + 4)); }
};
-// TODO: remove these bogus operators
-constexpr bool operator==(const cpp17_input_iterator<const CopyableChild*> &lhs, const CopyableChild *rhs) { return base(lhs) == rhs; }
-constexpr bool operator==(const CopyableChild *lhs, const cpp17_input_iterator<const CopyableChild*> &rhs) { return base(rhs) == lhs; }
-struct Box { int x; };
+struct Box {
+ int x;
+};
-template<class T>
+template <class T>
struct InputValueIter {
typedef std::input_iterator_tag iterator_category;
typedef T value_type;
typedef int
diff erence_type;
typedef T reference;
- T *ptr_;
- constexpr InputValueIter(T *ptr) : ptr_(ptr) {}
+ T* ptr_ = nullptr;
+ constexpr InputValueIter() = default;
+ constexpr InputValueIter(T* ptr) : ptr_(ptr) {}
constexpr T operator*() const { return std::move(*ptr_); }
constexpr void operator++(int) { ++ptr_; }
- constexpr InputValueIter& operator++() { ++ptr_; return *this; }
+ constexpr InputValueIter& operator++() {
+ ++ptr_;
+ return *this;
+ }
- constexpr T *operator->() { return ptr_; }
-};
+ constexpr T* operator->() { return ptr_; }
-template<class T>
-constexpr bool operator==(const InputValueIter<T> &lhs, const T* rhs) { return lhs.ptr_ == rhs; }
-template<class T>
-constexpr bool operator==(const T* lhs, const InputValueIter<T> &rhs) { return rhs.ptr_ == lhs; }
+ constexpr friend bool operator==(const InputValueIter&, const InputValueIter&) = default;
+};
-template<class T>
+template <class T>
struct ValueView : std::ranges::view_base {
InputValueIter<T> ptr_;
- constexpr ValueView(T *ptr) : ptr_(ptr) {}
+ using sentinel = sentinel_wrapper<InputValueIter<T>>;
- constexpr ValueView(ValueView &&other)
- : ptr_(other.ptr_) { other.ptr_.ptr_ = nullptr; }
+ constexpr ValueView(T* ptr) : ptr_(ptr) {}
- constexpr ValueView& operator=(ValueView &&other) {
+ constexpr ValueView(ValueView&& other) : ptr_(other.ptr_) { other.ptr_.ptr_ = nullptr; }
+
+ constexpr ValueView& operator=(ValueView&& other) {
ptr_ = other.ptr_;
other.ptr_ = InputValueIter<T>(nullptr);
return *this;
@@ -132,10 +153,260 @@ struct ValueView : std::ranges::view_base {
ValueView(const ValueView&) = delete;
ValueView& operator=(const ValueView&) = delete;
- constexpr InputValueIter<T> begin() { return ptr_; }
constexpr InputValueIter<T> begin() const { return ptr_; }
- constexpr T *end() { return ptr_.ptr_ + 4; }
- constexpr const T *end() const { return ptr_.ptr_ + 4; }
+ constexpr sentinel end() const { return sentinel(InputValueIter<T>(ptr_.ptr_ + 4)); }
+};
+
+template <class Iter, class Sent = Iter, class NonConstIter = Iter, class NonConstSent = Sent>
+struct BufferView : std::ranges::view_base {
+
+ using T = std::iter_value_t<Iter>;
+ T* data_;
+ std::size_t size_;
+
+ template <std::size_t N>
+ constexpr BufferView(T (&b)[N]) : data_(b), size_(N) {}
+ constexpr BufferView(T* p, std::size_t s) : data_(p), size_(s) {}
+
+ constexpr NonConstIter begin()
+ requires(!std::is_same_v<Iter, NonConstIter>) {
+ return NonConstIter(this->data_);
+ }
+ constexpr Iter begin() const { return Iter(this->data_); }
+
+ constexpr NonConstSent end()
+ requires(!std::is_same_v<Sent, NonConstSent>) {
+ if constexpr (std::is_same_v<NonConstIter, NonConstSent>) {
+ return NonConstIter(this->data_ + this->size_);
+ } else {
+ return NonConstSent(NonConstIter(this->data_ + this->size_));
+ }
+ }
+
+ constexpr Sent end() const {
+ if constexpr (std::is_same_v<Iter, Sent>) {
+ return Iter(this->data_ + this->size_);
+ } else {
+ return Sent(Iter(this->data_ + this->size_));
+ }
+ }
+};
+
+// an `input_iterator` that can be used in a `common_range`
+template <class Base>
+struct common_input_iterator {
+ Base it_;
+
+ using value_type = std::iter_value_t<Base>;
+ using
diff erence_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<Base> operator*() const { return *it_; }
+
+ friend constexpr bool operator==(common_input_iterator const&, common_input_iterator const&) = default;
+};
+
+using InputCommonInner = BufferView<common_input_iterator<int*>>;
+static_assert(std::ranges::input_range<InputCommonInner>);
+static_assert(!std::ranges::forward_range<InputCommonInner>);
+static_assert(std::ranges::common_range<InputCommonInner>);
+
+using InputNonCommonInner = BufferView<common_input_iterator<int*>, sentinel_wrapper<common_input_iterator<int*>>>;
+static_assert(std::ranges::input_range<InputNonCommonInner>);
+static_assert(!std::ranges::forward_range<InputNonCommonInner>);
+static_assert(!std::ranges::common_range<InputNonCommonInner>);
+
+using ForwardCommonInner = BufferView<forward_iterator<int*>>;
+static_assert(std::ranges::forward_range<ForwardCommonInner>);
+static_assert(!std::ranges::bidirectional_range<ForwardCommonInner>);
+static_assert(std::ranges::common_range<ForwardCommonInner>);
+
+using ForwardNonCommonInner = BufferView<forward_iterator<int*>, sentinel_wrapper<forward_iterator<int*>>>;
+static_assert(std::ranges::forward_range<ForwardNonCommonInner>);
+static_assert(!std::ranges::bidirectional_range<ForwardNonCommonInner>);
+static_assert(!std::ranges::common_range<ForwardNonCommonInner>);
+
+using BidiCommonInner = BufferView<bidirectional_iterator<int*>>;
+static_assert(std::ranges::bidirectional_range<BidiCommonInner>);
+static_assert(std::ranges::common_range<BidiCommonInner>);
+
+using BidiNonCommonInner = BufferView<bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>;
+static_assert(std::ranges::bidirectional_range<BidiNonCommonInner>);
+static_assert(!std::ranges::common_range<BidiNonCommonInner>);
+
+template <class Inner = BufferView<int*>>
+using SimpleInputCommonOuter = BufferView<common_input_iterator<Inner*>>;
+static_assert(!std::ranges::forward_range<SimpleInputCommonOuter<>>);
+static_assert(!std::ranges::bidirectional_range<SimpleInputCommonOuter<>>);
+static_assert(std::ranges::common_range<SimpleInputCommonOuter<>>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleInputCommonOuter<>>);
+
+template <class Inner = BufferView<int*>>
+using NonSimpleInputCommonOuter = BufferView<common_input_iterator<const Inner*>, common_input_iterator<const Inner*>,
+ common_input_iterator< Inner*>, common_input_iterator< Inner*>>;
+static_assert(!std::ranges::forward_range<NonSimpleInputCommonOuter<>>);
+static_assert(!std::ranges::bidirectional_range<NonSimpleInputCommonOuter<>>);
+static_assert(std::ranges::common_range<NonSimpleInputCommonOuter<>>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleInputCommonOuter<>>);
+
+template <class Inner = BufferView<int*>>
+using SimpleForwardCommonOuter = BufferView<forward_iterator<Inner*>>;
+static_assert(std::ranges::forward_range<SimpleForwardCommonOuter<>>);
+static_assert(!std::ranges::bidirectional_range<SimpleForwardCommonOuter<>>);
+static_assert(std::ranges::common_range<SimpleForwardCommonOuter<>>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleForwardCommonOuter<>>);
+
+template <class Inner = BufferView<int*>>
+using NonSimpleForwardCommonOuter = BufferView<forward_iterator<const Inner*>, forward_iterator<const Inner*>,
+ forward_iterator<Inner*>, forward_iterator<Inner*>>;
+static_assert(std::ranges::forward_range<NonSimpleForwardCommonOuter<>>);
+static_assert(!std::ranges::bidirectional_range<NonSimpleForwardCommonOuter<>>);
+static_assert(std::ranges::common_range<NonSimpleForwardCommonOuter<>>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleForwardCommonOuter<>>);
+
+template <class Inner = BufferView<int*>>
+using SimpleForwardNonCommonOuter = BufferView<forward_iterator<Inner*>, sentinel_wrapper<forward_iterator<Inner*>>>;
+static_assert(std::ranges::forward_range<SimpleForwardNonCommonOuter<>>);
+static_assert(!std::ranges::bidirectional_range<SimpleForwardNonCommonOuter<>>);
+static_assert(!std::ranges::common_range<SimpleForwardNonCommonOuter<>>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleForwardNonCommonOuter<>>);
+
+template <class Inner = BufferView<int*>>
+using NonSimpleForwardNonCommonOuter =
+ BufferView<forward_iterator<const Inner*>, sentinel_wrapper<forward_iterator<const Inner*>>,
+ forward_iterator<Inner*>, sentinel_wrapper<forward_iterator<Inner*>>>;
+static_assert(std::ranges::forward_range<NonSimpleForwardNonCommonOuter<>>);
+static_assert(!std::ranges::bidirectional_range<NonSimpleForwardNonCommonOuter<>>);
+static_assert(!std::ranges::common_range<NonSimpleForwardNonCommonOuter<>>);
+LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleForwardNonCommonOuter<>>);
+
+template <class Inner = BufferView<int*>>
+using BidiCommonOuter = BufferView<bidirectional_iterator<Inner*>>;
+static_assert(std::ranges::bidirectional_range<BidiCommonOuter<>>);
+static_assert(std::ranges::common_range<BidiCommonOuter<>>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<BidiCommonOuter<>>);
+
+// an iterator where its operator* makes a copy of underlying operator*
+template <class It>
+struct copying_iterator {
+ It it_ = It();
+
+ using value_type = typename std::iterator_traits<It>::value_type;
+ using
diff erence_type = typename std::iterator_traits<It>::
diff erence_type;
+ using pointer = typename std::iterator_traits<It>::pointer;
+
+ copying_iterator() requires std::default_initializable<It> = default;
+ constexpr copying_iterator(It it) : it_(std::move(it)) {}
+
+ // makes a copy of underlying operator* to create a PRValue
+ constexpr value_type operator*() const { return *it_; }
+
+ constexpr copying_iterator& operator++() {
+ ++it_;
+ return *this;
+ }
+ constexpr copying_iterator& operator--()
+ requires std::bidirectional_iterator<It> {
+ --it_;
+ return *this;
+ }
+ constexpr copying_iterator operator++(int)
+ requires std::forward_iterator<It> {
+ return copying_iterator(it_++);
+ }
+ constexpr void operator++(int) { return it_++; }
+ constexpr copying_iterator operator--(int)
+ requires std::bidirectional_iterator<It> {
+ return copying_iterator(it_--);
+ }
+
+ friend constexpr bool operator==(const copying_iterator& x, const copying_iterator& y) = default;
+};
+
+template <class Outer>
+struct InnerRValue : Outer {
+
+ using iterator = copying_iterator<std::ranges::iterator_t<Outer>>;
+ using const_iterator = copying_iterator<std::ranges::iterator_t<const Outer>>;
+ using sentinel = copying_iterator<std::ranges::sentinel_t<Outer>>;
+ using const_sentinel = copying_iterator<std::ranges::sentinel_t<const Outer>>;
+
+ using Outer::Outer;
+ static_assert(std::ranges::common_range<Outer>, "non-common range is not supported yet");
+
+ constexpr iterator begin() { return Outer::begin(); }
+ constexpr const_iterator begin() const
+ requires std::ranges::range<const Outer> {
+ return Outer::begin();
+ }
+
+ constexpr auto end() { return iterator{Outer::end()}; }
+ constexpr auto end() const
+ requires std::ranges::range<const Outer> {
+ return const_iterator{Outer::end()};
+ }
+};
+static_assert(std::ranges::forward_range<InnerRValue<SimpleForwardCommonOuter<>>>);
+static_assert(!std::ranges::bidirectional_range<InnerRValue<SimpleForwardCommonOuter<>>>);
+static_assert(std::ranges::common_range<InnerRValue<SimpleForwardCommonOuter<>>>);
+LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<InnerRValue<SimpleForwardCommonOuter<>>>);
+static_assert(!std::is_lvalue_reference_v<std::ranges::range_reference_t<InnerRValue<SimpleForwardCommonOuter<>>>>);
+
+struct move_swap_aware_iter {
+
+ // This is a proxy-like iterator where `reference` is a prvalue, and
+ // `reference` and `value_type` are distinct types (similar to `zip_view::iterator`).
+ using value_type = std::pair<int, int>;
+ using reference = std::pair<int&, int&>;
+ using rvalue_reference = std::pair<int&&, int&&>;
+
+ using
diff erence_type = std::intptr_t;
+ using iterator_concept = std::input_iterator_tag;
+
+ int* iter_move_called = nullptr;
+ int* iter_swap_called = nullptr;
+ int* i_ = nullptr;
+
+ constexpr move_swap_aware_iter& operator++() {
+ ++i_;
+ return *this;
+ }
+ constexpr void operator++(int) { ++i_; }
+
+ constexpr reference operator*() const { return reference(*i_, *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_), 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 : BufferView<int*> {
+ int iter_move_called = 0;
+ int iter_swap_called = 0;
+ using BufferView<int*>::BufferView;
+
+ constexpr auto begin() { return move_swap_aware_iter{&iter_move_called, &iter_swap_called, data_}; }
+
+ constexpr auto end() { return move_swap_aware_iter{&iter_move_called, &iter_swap_called, data_ + size_}; }
};
+static_assert(std::ranges::input_range<IterMoveSwapAwareView>);
#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H
More information about the libcxx-commits
mailing list