1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 // UNSUPPORTED: libcpp-has-no-incomplete-ranges
11 
12 // std::views::join
13 
14 #include <ranges>
15 
16 #include <cassert>
17 #include <type_traits>
18 
19 #include "types.h"
20 
21 struct MoveOnlyOuter : SimpleForwardCommonOuter<ForwardCommonInner> {
22   using SimpleForwardCommonOuter<ForwardCommonInner>::SimpleForwardCommonOuter;
23 
24   constexpr MoveOnlyOuter(MoveOnlyOuter&&) = default;
25   constexpr MoveOnlyOuter(const MoveOnlyOuter&) = delete;
26 
27   constexpr MoveOnlyOuter& operator=(MoveOnlyOuter&&) = default;
28   constexpr MoveOnlyOuter& operator=(const MoveOnlyOuter&) = delete;
29 };
30 
31 struct Foo {
32   int i;
FooFoo33   constexpr Foo(int ii) : i(ii) {}
34 };
35 
36 template <class View, class T>
37 concept CanBePiped = requires(View&& view, T&& t) {
38   { std::forward<View>(view) | std::forward<T>(t) };
39 };
40 
test()41 constexpr bool test() {
42   int buffer1[3] = {1, 2, 3};
43   int buffer2[2] = {4, 5};
44   int buffer3[4] = {6, 7, 8, 9};
45   Foo nested[2][3][3] = {{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}, {{10, 11, 12}, {13, 14, 15}, {16, 17, 18}}};
46 
47   {
48     // Test `views::join(v)`
49     ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3};
50     using Result = std::ranges::join_view<std::ranges::ref_view<ForwardCommonInner[3]>>;
51     std::same_as<Result> decltype(auto) v = std::views::join(inners);
52     assert(std::ranges::next(v.begin(), 9) == v.end());
53     assert(&(*v.begin()) == buffer1);
54   }
55 
56   {
57     // Test `views::join(move-only-view)`
58     ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3};
59     using Result = std::ranges::join_view<MoveOnlyOuter>;
60     std::same_as<Result> decltype(auto) v = std::views::join(MoveOnlyOuter{inners});
61     assert(std::ranges::next(v.begin(), 9) == v.end());
62     assert(&(*v.begin()) == buffer1);
63 
64     static_assert(std::invocable<decltype(std::views::join), MoveOnlyOuter>);
65     static_assert(!std::invocable<decltype(std::views::join), MoveOnlyOuter&>);
66   }
67 
68   {
69     // LWG3474 Nesting `join_views` is broken because of CTAD
70     // views::join(join_view) should join the view instead of calling copy constructor
71     auto jv = std::views::join(nested);
72     ASSERT_SAME_TYPE(std::ranges::range_reference_t<decltype(jv)>, Foo(&)[3]);
73 
74     auto jv2 = std::views::join(jv);
75     ASSERT_SAME_TYPE(std::ranges::range_reference_t<decltype(jv2)>, Foo&);
76 
77     assert(&(*jv2.begin()) == &nested[0][0][0]);
78   }
79 
80   {
81     // Test `v | views::join`
82     ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3};
83 
84     using Result = std::ranges::join_view<std::ranges::ref_view<ForwardCommonInner[3]>>;
85     std::same_as<Result> decltype(auto) v = inners | std::views::join;
86     assert(std::ranges::next(v.begin(), 9) == v.end());
87     assert(&(*v.begin()) == buffer1);
88     static_assert(CanBePiped<decltype((inners)), decltype((std::views::join))>);
89   }
90 
91   {
92     // Test `move-only-view | views::join`
93     ForwardCommonInner inners[3] = {buffer1, buffer2, buffer3};
94     using Result = std::ranges::join_view<MoveOnlyOuter>;
95     std::same_as<Result> decltype(auto) v = MoveOnlyOuter{inners} | std::views::join;
96     assert(std::ranges::next(v.begin(), 9) == v.end());
97     assert(&(*v.begin()) == buffer1);
98 
99     static_assert(CanBePiped<MoveOnlyOuter, decltype((std::views::join))>);
100     static_assert(!CanBePiped<MoveOnlyOuter&, decltype((std::views::join))>);
101   }
102 
103   {
104     // LWG3474 Nesting `join_views` is broken because of CTAD
105     // join_view | views::join should join the view instead of calling copy constructor
106     auto jv = nested | std::views::join | std::views::join;
107     ASSERT_SAME_TYPE(std::ranges::range_reference_t<decltype(jv)>, Foo&);
108 
109     assert(&(*jv.begin()) == &nested[0][0][0]);
110     static_assert(CanBePiped<decltype((nested)), decltype((std::views::join))>);
111   }
112 
113   {
114     // Test `adaptor | views::join`
115     auto join_twice = std::views::join | std::views::join;
116     auto jv = nested | join_twice;
117     ASSERT_SAME_TYPE(std::ranges::range_reference_t<decltype(jv)>, Foo&);
118 
119     assert(&(*jv.begin()) == &nested[0][0][0]);
120     static_assert(CanBePiped<decltype((nested)), decltype((join_twice))>);
121   }
122 
123   {
124     static_assert(!CanBePiped<int, decltype((std::views::join))>);
125     static_assert(!CanBePiped<Foo, decltype((std::views::join))>);
126     static_assert(!CanBePiped<int(&)[2], decltype((std::views::join))>);
127     static_assert(CanBePiped<int(&)[2][2], decltype((std::views::join))>);
128   }
129 
130   return true;
131 }
132 
main(int,char **)133 int main(int, char**) {
134   test();
135   static_assert(test());
136 
137   return 0;
138 }
139