Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[libc++] Implement views::join_with
Browse files Browse the repository at this point in the history
* Implement "P2441R2 `views::join_with`" (https://wg21.link/P2441R2)
* Complete implementation of "P2711R1 Making multi-param constructors of views explicit" (https://wg21.link/P2711R1)
* Complete implementation of "P2770R0 Stashing stashing iterators for proper flattening" (https://wg21.link/P2770R0)
JMazurkiewicz committed May 22, 2024

Verified

This commit was signed with the committer’s verified signature.
JMazurkiewicz Jakub Mazurkiewicz
1 parent 465bfd4 commit a9a1031
Showing 54 changed files with 5,385 additions and 57 deletions.
2 changes: 1 addition & 1 deletion libcxx/docs/FeatureTestMacroTable.rst
Original file line number Diff line number Diff line change
@@ -362,7 +362,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_iota`` *unimplemented*
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_join_with`` *unimplemented*
``__cpp_lib_ranges_join_with`` ``202202L``
---------------------------------------------------------- -----------------
``__cpp_lib_ranges_repeat`` ``202207L``
---------------------------------------------------------- -----------------
3 changes: 3 additions & 0 deletions libcxx/docs/ReleaseNotes/19.rst
Original file line number Diff line number Diff line change
@@ -54,6 +54,9 @@ Implemented Papers
- P2713R1 - Escaping improvements in ``std::format``
- P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``
- P0019R8 - ``std::atomic_ref``
- P2441R2 - ``views::join_with``
- P2711R1 - Making multi-param constructors of ``views`` ``explicit``
- P2770R0 - Stashing stashing ``iterators`` for proper flattening

Improvements and New Features
-----------------------------
4 changes: 1 addition & 3 deletions libcxx/docs/Status/Cxx23.rst
Original file line number Diff line number Diff line change
@@ -44,8 +44,6 @@ Paper Status
.. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
clang doesn't issue a diagnostic for deprecated using template declarations.
.. [#note-P2520R0] P2520R0: Libc++ implemented this paper as a DR in C++20 as well.
.. [#note-P2711R1] P2711R1: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
.. [#note-P2770R0] P2770R0: ``join_with_view`` hasn't been done yet since this type isn't implemented yet.
.. [#note-P2693R1] P2693R1: The formatter for ``std::thread::id`` is implemented.
The formatter for ``stacktrace`` is not implemented, since ``stacktrace`` is
not implemented yet.
@@ -63,5 +61,5 @@ Library Working Group Issues Status
.. note::

.. [#note-LWG3750] LWG3750 Only ``__cpp_lib_format_ranges`` is fully implemented.
.. [#note-LWG3798] LWG3798: ``join_with_view``, ``zip_transform_view``, and ``adjacent_transform_view`` haven't been done yet since these types aren't implemented yet.
.. [#note-LWG3798] LWG3798: ``zip_transform_view`` and ``adjacent_transform_view`` haven't been done yet since these types aren't implemented yet.
.. [#note-LWG3036] LWG3036: This issue was reverted by P2875R4
6 changes: 3 additions & 3 deletions libcxx/docs/Status/Cxx23Papers.csv
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@
"`P2273R3 <https://wg21.link/P2273R3>`__","LWG","Making ``std::unique_ptr`` constexpr","February 2022","|Complete|","16.0"
"`P2387R3 <https://wg21.link/P2387R3>`__","LWG","Pipe support for user-defined range adaptors","February 2022","|Complete|","19.0","|ranges|"
"`P2440R1 <https://wg21.link/P2440R1>`__","LWG","``ranges::iota``, ``ranges::shift_left`` and ``ranges::shift_right``","February 2022","","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|In Progress|","","|ranges|"
"`P2441R2 <https://wg21.link/P2441R2>`__","LWG","``views::join_with``","February 2022","|Complete|","19.0","|ranges|"
"`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
"`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
"","","","","","",""
@@ -104,9 +104,9 @@
"`P2708R1 <https://wg21.link/P2708R1>`__","LWG", "No Further Fundamentals TSes", "November 2022","|Nothing to do|","",""
"","","","","","",""
"`P0290R4 <https://wg21.link/P0290R4>`__","LWG", "``apply()`` for ``synchronized_value<T>``","February 2023","","","|concurrency TS|"
"`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|Partial| [#note-P2770R0]_","","|ranges|"
"`P2770R0 <https://wg21.link/P2770R0>`__","LWG", "Stashing stashing ``iterators`` for proper flattening","February 2023","|Complete|","19.0","|ranges|"
"`P2164R9 <https://wg21.link/P2164R9>`__","LWG", "``views::enumerate``","February 2023","","","|ranges|"
"`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|In Progress| [#note-P2711R1]_","","|ranges|"
"`P2711R1 <https://wg21.link/P2711R1>`__","LWG", "Making multi-param constructors of ``views`` ``explicit``","February 2023","|Complete|","19.0","|ranges|"
"`P2609R3 <https://wg21.link/P2609R3>`__","LWG", "Relaxing Ranges Just A Smidge","February 2023","","","|ranges|"
"`P2713R1 <https://wg21.link/P2713R1>`__","LWG", "Escaping improvements in ``std::format``","February 2023","|Complete|","19.0","|format|"
"`P2675R1 <https://wg21.link/P2675R1>`__","LWG", "``format``'s width estimation is too approximate and not forward compatible","February 2023","|Complete|","17.0","|format|"
2 changes: 1 addition & 1 deletion libcxx/docs/Status/RangesMajorFeatures.csv
Original file line number Diff line number Diff line change
@@ -2,4 +2,4 @@ Standard,Name,Assignee,CL,Status
C++23,`ranges::to <https://wg21.link/P1206R7>`_,Konstantin Varlamov,`D142335 <https://reviews.llvm.org/D142335>`_,Complete
C++23,`Pipe support for user-defined range adaptors <https://wg21.link/P2387R3>`_,"Louis Dionne, Jakub Mazurkiewicz, and Xiaoyang Liu",Various,Complete
C++23,`Formatting Ranges <https://wg21.link/P2286R8>`_,Mark de Wever,Various,Complete
C++20,`Stashing stashing iterators for proper flattening <https://wg21.link/P2770R0>`_,Jakub Mazurkiewicz,Various,In progress
C++20,`Stashing stashing iterators for proper flattening <https://wg21.link/P2770R0>`_,Jakub Mazurkiewicz,Various,Complete
2 changes: 1 addition & 1 deletion libcxx/docs/Status/RangesViews.csv
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@ C++23,`zip <https://wg21.link/P2321R2>`_,Hui Xie,`D122806 <https://llvm.org/D122
C++23,`zip_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`adjacent <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`adjacent_transform <https://wg21.link/P2321R2>`_,Hui Xie,No patch yet,Not started
C++23,`join_with <https://wg21.link/P2441R2>`_,Jakub Mazurkiewicz,`65536 <https://github.com/llvm/llvm-project/pull/65536>`_,In progress
C++23,`join_with <https://wg21.link/P2441R2>`_,Jakub Mazurkiewicz,`65536 <https://github.com/llvm/llvm-project/pull/65536>`_,
C++23,`slide <https://wg21.link/P2442R1>`_,Will Hawkins,`67146 <https://github.com/llvm/llvm-project/pull/67146>`_,In Progress
C++23,`chunk <https://wg21.link/P2442R1>`_,Unassigned,No patch yet,Not started
C++23,`chunk_by <https://wg21.link/P2443R1>`_,Jakub Mazurkiewicz,`D144767 <https://llvm.org/D144767>`_,✅
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -659,6 +659,7 @@ set(files
__ranges/iota_view.h
__ranges/istream_view.h
__ranges/join_view.h
__ranges/join_with_view.h
__ranges/lazy_split_view.h
__ranges/movable_box.h
__ranges/non_propagating_cache.h
456 changes: 456 additions & 0 deletions libcxx/include/__ranges/join_with_view.h

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions libcxx/include/module.modulemap
Original file line number Diff line number Diff line change
@@ -1728,6 +1728,10 @@ module std_private_ranges_join_view [system] {
export std_private_iterator_iterator_with_data
export std_private_iterator_segmented_iterator
}
module std_private_ranges_join_with_view [system] {
header "__ranges/join_with_view.h"
export std_variant
}
module std_private_ranges_lazy_split_view [system] {
header "__ranges/lazy_split_view.h"
export std_private_ranges_non_propagating_cache
13 changes: 13 additions & 0 deletions libcxx/include/ranges
Original file line number Diff line number Diff line change
@@ -285,6 +285,18 @@ namespace std::ranges {
requires view<V> && input_range<range_reference_t<V>>
class join_view;
// [range.join.with], join with view
template<class R, class P>
concept compatible-joinable-ranges = see below; // exposition only
template<input_range V, forward_range Pattern>
requires view<V> && input_range<range_reference_t<V>>
&& view<Pattern>
&& compatible-joinable-ranges<range_reference_t<V>, Pattern>
class join_with_view; // since C++23
namespace views { inline constexpr unspecified join_with = unspecified; } // since C++23
// [range.lazy.split], lazy split view
template<class R>
concept tiny-range = see below; // exposition only
@@ -401,6 +413,7 @@ namespace std {
#include <__ranges/from_range.h>
#include <__ranges/iota_view.h>
#include <__ranges/join_view.h>
#include <__ranges/join_with_view.h>
#include <__ranges/lazy_split_view.h>
#include <__ranges/rbegin.h>
#include <__ranges/ref_view.h>
2 changes: 1 addition & 1 deletion libcxx/include/version
Original file line number Diff line number Diff line change
@@ -478,7 +478,7 @@ __cpp_lib_void_t 201411L <type_traits>
# define __cpp_lib_ranges_chunk_by 202202L
# define __cpp_lib_ranges_contains 202207L
// # define __cpp_lib_ranges_iota 202202L
// # define __cpp_lib_ranges_join_with 202202L
# define __cpp_lib_ranges_join_with 202202L
# define __cpp_lib_ranges_repeat 202207L
// # define __cpp_lib_ranges_slide 202202L
# define __cpp_lib_ranges_starts_ends_with 202106L
7 changes: 5 additions & 2 deletions libcxx/modules/std/ranges.inc
Original file line number Diff line number Diff line change
@@ -223,13 +223,16 @@ export namespace std {
namespace views {
using std::ranges::views::join;
} // namespace views
#if 0

#if _LIBCPP_STD_VER >= 23
// [range.join.with]
using std::ranges::join_with_view;

namespace views {
using std::ranges::views::join_with;
} // namespace views
#endif
#endif // _LIBCPP_STD_VER >= 23

using std::ranges::lazy_split_view;

// [range.split], split view
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::operator* is marked as [[nodiscard]].

#include <ranges>
#include <utility>

void test() {
char range[3][2] = {{'x', 'x'}, {'y', 'y'}, {'z', 'z'}};
char pattern[2] = {',', ' '};

std::ranges::join_with_view view(range, pattern);

// clang-format off
*view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
*std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// clang-format on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// <ranges>

// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::operator== is marked as [[nodiscard]].

#include <ranges>
#include <utility>

void test() {
char16_t range[3][1] = {{u'x'}, {u'y'}, {u'z'}};
char16_t pattern[1] = {u'-'};

std::ranges::join_with_view view(range, pattern);

// clang-format off
(view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
(std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
(view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
(std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// clang-format on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// Test the libc++ extension that std::ranges::join_with_view::iterator<Const>::iter_move is marked as [[nodiscard]].

#include <ranges>
#include <utility>

void test() {
long range[2][1] = {{0L}, {2L}};
long pattern[1] = {1L};

std::ranges::join_with_view view(range, pattern);

// clang-format off
iter_move(view.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
iter_move(std::as_const(view).begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// clang-format on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20
// XFAIL: msvc && clang-17

// <ranges>

// This test ensures that we use `[[no_unique_address]]` in `join_with_view::iterator`.

#include <ranges>

#include "test_iterators.h"
#include "test_range.h"

struct InputView : std::ranges::view_base {
using Inner = test_range<forward_iterator>;

cpp20_input_iterator<Inner*> begin();
sentinel_wrapper<cpp20_input_iterator<Inner*>> end();
};

static_assert(std::ranges::input_range<InputView>);
static_assert(!std::ranges::forward_range<InputView>);

struct Pattern : std::ranges::view_base {
int* begin();
int* end();
};

static_assert(alignof(void*) == alignof(std::variant<int*, int*>)); // alignof(__parent_) == alignof(__inner_it_)
static_assert(sizeof(std::ranges::iterator_t<std::ranges::join_with_view<InputView, Pattern>>) ==
sizeof(void*) + sizeof(std::variant<int*, int*>)); // sizeof(__parent_) + sizeof(__inner_it_)
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// Test the libc++ extension that std::views::join_with is marked as [[nodiscard]].

#include <ranges>

void test() {
int range[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int pattern_base[2] = {-1, -1};
auto pattern = std::views::all(pattern_base);

// clang-format off
std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::join_with(range, pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
range | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::reverse | std::views::join_with(pattern); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}

std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::join_with(range, 0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
range | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::views::reverse | std::views::join_with(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// clang-format on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// Test the libc++ extension that std::ranges::join_with_view::sentinel<Const>::operator== is marked as [[nodiscard]].

#include <array>
#include <ranges>
#include <utility>

#include "test_iterators.h"
#include "test_range.h"

void test() {
std::array<test_range<cpp20_input_iterator>, 0> range;
std::array<int, 0> pattern;

std::ranges::join_with_view view(range, pattern);
static_assert(!std::ranges::common_range<decltype(view)>);

// clang-format off
(view.begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
(std::as_const(view).begin() == view.end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
(view.begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
(std::as_const(view).begin() == std::as_const(view).end()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// clang-format on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// <ranges>

// Test the libc++ extension that std::ranges::join_with_view::base is marked as [[nodiscard]].

#include <ranges>
#include <utility>

void test() {
int range[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int pattern[2] = {-1, -1};

std::ranges::join_with_view view(range, pattern);

// clang-format off
view.base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::as_const(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::move(std::as_const(view)).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::move(view).base(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// clang-format on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// Test the libc++ extension that std::ranges::join_with_view::begin is marked as [[nodiscard]].

#include <ranges>
#include <utility>

void test() {
int range[3][2] = {{1, 3}, {4, 6}, {7, 9}};
int pattern[1] = {-2};

std::ranges::join_with_view view(range, pattern);

// clang-format off
view.begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::as_const(view).begin(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// clang-format on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// Test the libc++ extension that std::ranges::join_with_view::end is marked as [[nodiscard]].

#include <ranges>
#include <utility>

void test() {
int range[3][2] = {{1, 2}, {4, 5}, {7, 8}};
int pattern[1] = {-3};

std::ranges::join_with_view view(range, pattern);

// clang-format off
view.end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
std::as_const(view).end(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
// clang-format on
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// XFAIL: msvc

// <ranges>

// This test ensures that we use `[[no_unique_address]]` in `join_with_view`.

#include <ranges>
#include <string>

struct ForwardView : std::ranges::view_base {
std::string* begin() const;
std::string* end() const;
};

static_assert(std::ranges::forward_range<ForwardView>);
static_assert(std::is_reference_v<std::ranges::range_reference_t<ForwardView>>);

struct Pattern : std::ranges::view_base {
char* begin() const;
char* end() const;
};

template <class View>
struct Test {
[[no_unique_address]] View view;
char c;
};

static_assert(sizeof(Test<std::ranges::join_with_view<ForwardView, Pattern>>) ==
sizeof(std::ranges::join_with_view<ForwardView, Pattern>));
1 change: 1 addition & 0 deletions libcxx/test/libcxx/transitive_includes/cxx03.csv
Original file line number Diff line number Diff line change
@@ -669,6 +669,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
ranges variant
ranges version
ratio climits
ratio cstdint
1 change: 1 addition & 0 deletions libcxx/test/libcxx/transitive_includes/cxx11.csv
Original file line number Diff line number Diff line change
@@ -674,6 +674,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
ranges variant
ranges version
ratio climits
ratio cstdint
1 change: 1 addition & 0 deletions libcxx/test/libcxx/transitive_includes/cxx14.csv
Original file line number Diff line number Diff line change
@@ -677,6 +677,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
ranges variant
ranges version
ratio climits
ratio cstdint
1 change: 1 addition & 0 deletions libcxx/test/libcxx/transitive_includes/cxx17.csv
Original file line number Diff line number Diff line change
@@ -677,6 +677,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
ranges variant
ranges version
ratio climits
ratio cstdint
1 change: 1 addition & 0 deletions libcxx/test/libcxx/transitive_includes/cxx20.csv
Original file line number Diff line number Diff line change
@@ -688,6 +688,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
ranges variant
ranges version
ratio climits
ratio cstdint
Original file line number Diff line number Diff line change
@@ -293,17 +293,11 @@
# error "__cpp_lib_ranges_concat should not be defined before c++26"
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++23"
# endif
# if __cpp_lib_ranges_join_with != 202202L
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++23"
# endif
# if __cpp_lib_ranges_join_with != 202202L
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
# endif

# ifndef __cpp_lib_ranges_repeat
@@ -421,17 +415,11 @@
# endif
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++26"
# endif
# if __cpp_lib_ranges_join_with != 202202L
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++26"
# endif
# if __cpp_lib_ranges_join_with != 202202L
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
# endif

# ifndef __cpp_lib_ranges_repeat
Original file line number Diff line number Diff line change
@@ -5567,17 +5567,11 @@
# endif
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++23"
# endif
# if __cpp_lib_ranges_join_with != 202202L
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++23"
# endif
# if __cpp_lib_ranges_join_with != 202202L
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++23"
# endif

# ifndef __cpp_lib_ranges_repeat
@@ -7377,17 +7371,11 @@
# endif
# endif

# if !defined(_LIBCPP_VERSION)
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++26"
# endif
# if __cpp_lib_ranges_join_with != 202202L
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
# endif
# else // _LIBCPP_VERSION
# ifdef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should not be defined because it is unimplemented in libc++!"
# endif
# ifndef __cpp_lib_ranges_join_with
# error "__cpp_lib_ranges_join_with should be defined in c++26"
# endif
# if __cpp_lib_ranges_join_with != 202202L
# error "__cpp_lib_ranges_join_with should have the value 202202L in c++26"
# endif

# ifndef __cpp_lib_ranges_repeat
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// iterator() = default;

#include <ranges>

#include <cassert>
#include <initializer_list>
#include <iterator>
#include <type_traits>
#include <utility>

#include "../types.h"
#include "test_comparisons.h"
#include "test_iterators.h"

constexpr bool test() {
{ // `V` and `Pattern` model forward range
using Inner = BasicVectorView<int, ViewProperties{}, forward_iterator>;
using V = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
using Pattern = Inner;
using JWV = std::ranges::join_with_view<V, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using ConstIter = std::ranges::iterator_t<const JWV>;

Iter iter;
assert(testEquality(iter, Iter{}, true));

ConstIter citer;
assert(testEquality(citer, ConstIter{}, true));
assert(testEquality(iter, citer, true));

std::ranges::join_with_view<V, Pattern> jwv(V{Inner{1, 2}, Inner{2, 1}}, Pattern{3, 3});
Iter jwv_iter = jwv.begin();
ConstIter jwv_citer = std::as_const(jwv).begin();
assert(testEquality(jwv_iter, jwv_citer, true));

assert(testEquality(jwv_iter, iter, false));
assert(testEquality(jwv_iter, citer, false));
assert(testEquality(jwv_citer, iter, false));
assert(testEquality(jwv_citer, citer, false));
}

{ // `InnerIter` is not default constructible (does not model forward iterator, JWV cannot be const-accessed)
using Inner = BasicVectorView<char, ViewProperties{.common = false}, EqComparableInputIter>;
using V = BasicVectorView<Inner, ViewProperties{.common = false}, forward_iterator>;
using Pattern = BasicVectorView<char, ViewProperties{}, forward_iterator>;
using JWV = std::ranges::join_with_view<V, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;

Iter iter;
assert(testEquality(iter, Iter{}, true));

std::ranges::join_with_view<V, Pattern> jwv(V{Inner{'a', 'b'}, Inner{'c', 'd'}}, Pattern{',', ' '});
Iter jwv_iter = jwv.begin();
assert(testEquality(jwv_iter, iter, false));
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// constexpr iterator(iterator<!Const> i)
// requires Const && convertible_to<iterator_t<V>, OuterIter> &&
// convertible_to<iterator_t<InnerRng>, InnerIter> &&
// convertible_to<iterator_t<Pattern>, PatternIter>;

#include <ranges>

#include <cassert>
#include <vector>

#include "../types.h"

constexpr bool test() {
{ // Regular conversion from `!Const` to `Const` iterator
std::vector<std::vector<int>> vec = {{1, 2}, {3, 4}, {5, 6}};
int pattern = 0;
std::ranges::join_with_view jwv(vec, pattern);
auto it = jwv.begin();
assert(*it == 1);

using CIter = std::ranges::iterator_t<const decltype(jwv)>;
const CIter cit1 = it; // `cit1` points to element of `V`
assert(*cit1 == 1);
assert(cit1 == it);

std::ranges::advance(it, 2);
assert(*it == 0);
CIter cit2 = it; // `cit2` points to element of `Pattern`
assert(*cit2 == 0);
assert(cit2 == it);

++it;
assert(*it == 3);
CIter cit3 = it;
assert(*cit3 == 3);
assert(cit3 == it);

--cit3;
assert(cit2 == cit3);
}

{ // Test conversion from `Const` to `!Const` (should be invalid)
using V = std::vector<std::vector<int>>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!std::convertible_to<CIter, Iter>);
static_assert(!std::constructible_from<Iter, CIter>);
}

{ // When `convertible_to<iterator_t<V>, OuterIter>` is not modeled
using Inner = std::vector<short>;
using V = ConstOppositeView<Inner>;
using Pattern = std::ranges::single_view<short>;
using JWV = std::ranges::join_with_view<V, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!std::convertible_to<CIter, Iter>);
static_assert(!std::constructible_from<Iter, CIter>);
}

{ // When `convertible_to<iterator_t<InnerRng>, InnerIter>` is not modeled
using Inner = ConstOppositeView<long>;
using V = std::vector<Inner>;
using Pattern = std::ranges::single_view<long>;
using JWV = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!std::convertible_to<CIter, Iter>);
static_assert(!std::constructible_from<Iter, CIter>);
}

{ // When `convertible_to<iterator_t<Pattern>, PatternIter>` is not modeled
using V = std::vector<std::vector<long long>>;
using Pattern = ConstOppositeView<long long>;
using JWV = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!std::convertible_to<CIter, Iter>);
static_assert(!std::constructible_from<Iter, CIter>);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// constexpr iterator& operator--()
// requires ref-is-glvalue && bidirectional_range<Base> &&
// bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
// constexpr iterator operator--(int)
// requires ref-is-glvalue && bidirectional_range<Base> &&
// bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;

#include <ranges>

#include <algorithm>
#include <array>
#include <cassert>
#include <forward_list>
#include <string>
#include <string_view>
#include <vector>

#include "../types.h"

template <class I>
concept CanPreDecrement = requires(I& i) {
{ --i } -> std::same_as<I&>;
};

template <class I>
concept CanPostDecrement = requires(I& i) {
{ i-- } -> std::same_as<I>;
};

template <class I>
concept CanDecrement = CanPreDecrement<I> && CanPostDecrement<I>;

constexpr bool test() {
{ // `V` and `Pattern` are not empty. Test return type too.
using V = std::ranges::owning_view<std::vector<std::string>>;
using Pattern = std::ranges::single_view<char>;
using JWV = std::ranges::join_with_view<V, Pattern>;

using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(CanDecrement<Iter>);
static_assert(CanDecrement<CIter>);

JWV jwv(V{{"01", "23", "45"}}, Pattern{'_'});

{
auto it = jwv.end();
std::same_as<Iter&> decltype(auto) it_ref = --it;
assert(it_ref == it);
assert(*it == '5');
std::same_as<Iter> decltype(auto) it_copy = it--;
assert(--it_copy == it);
--it;
assert(*it == '_');
it--;
assert(*it == '3');
--it;
it--;
assert(*it == '_');
}

{
auto cit = std::as_const(jwv).end();
std::same_as<CIter&> decltype(auto) cit_ref = --cit;
assert(cit_ref == cit);
assert(*cit == '5');
std::same_as<CIter> decltype(auto) cit_copy = cit--;
assert(--cit_copy == cit);
--cit;
assert(*cit == '_');
cit--;
assert(*cit == '3');
--cit;
cit--;
assert(*cit == '_');
}

assert(std::ranges::equal(std::views::reverse(std::move(jwv)), std::string_view{"54_32_10"}));
}

{ // `Pattern` is empty, `V` is not.
using Inner = std::array<int, 1>;
using V = std::ranges::owning_view<std::array<Inner, 3>>;
using Pattern = std::ranges::owning_view<std::array<int, 0>>;
using JWV = std::ranges::join_with_view<V, Pattern>;

JWV jwv(V{{Inner{-9}, Inner{-99}, Inner{-999}}}, Pattern{});

{
auto it = jwv.end();
--it;
assert(*it == -999);
it--;
assert(*it == -99);
--it;
assert(*it == -9);
assert(it == jwv.begin());
}

{
auto cit = std::as_const(jwv).end();
--cit;
assert(*cit == -999);
cit--;
assert(*cit == -99);
--cit;
assert(*cit == -9);
assert(cit == std::as_const(jwv).begin());
}
}

#if !defined(TEST_COMPILER_GCC) // GCC c++/101777
{ // `V` has empty subrange in the middle, `Pattern` is not empty. Try to go back and forth.
using V = std::array<std::vector<int>, 3>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;

JWV jwv(V{{{5}, {}, {125}}}, Pattern{1});

{
auto it = jwv.end();
--it;
assert(*it == 125);
it--;
assert(*it == 1);
--it;
assert(*it == 1);
it--;
assert(*it == 5);
++it;
assert(*it == 1);
--it;
assert(*it == 5);
std::ranges::advance(it, 4);
it--;
assert(*it == 125);
}

{
auto cit = std::as_const(jwv).end();
--cit;
assert(*cit == 125);
cit--;
assert(*cit == 1);
--cit;
assert(*cit == 1);
cit--;
assert(*cit == 5);
++cit;
assert(*cit == 1);
--cit;
assert(*cit == 5);
std::ranges::advance(cit, 4);
cit--;
assert(*cit == 125);
}
}

{ // Only first element of `V` is not empty. `Pattern` is empty. Try to go back and forth.
using Inner = std::vector<int>;
using V = std::ranges::owning_view<std::array<Inner, 3>>;
using Pattern = std::ranges::empty_view<int>;
using JWV = std::ranges::join_with_view<V, Pattern>;

JWV jwv(V{{Inner{999}, {}, {}}}, Pattern{});

{
auto it = jwv.end();
--it;
assert(*it == 999);
++it;
assert(it == jwv.end());
it--;
assert(*it == 999);
}

{
auto cit = std::as_const(jwv).end();
--cit;
assert(*cit == 999);
++cit;
assert(cit == std::as_const(jwv).end());
cit--;
assert(*cit == 999);
}
}
#endif // !defined(TEST_COMPILER_GCC)

{ // `ref-is-glvalue` is false
using V = RvalueVector<std::vector<int>>;
using Pattern = std::ranges::empty_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
using Iter = std::ranges::iterator_t<JWV>;
static_assert(!CanPreDecrement<Iter>);
static_assert(!CanPostDecrement<Iter>);
}

{ // `Base` does not model bidirectional range
using V = std::ranges::owning_view<std::forward_list<std::vector<int>>>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<V, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!CanPreDecrement<Iter>);
static_assert(!CanPostDecrement<Iter>);
static_assert(!CanPreDecrement<CIter>);
static_assert(!CanPostDecrement<CIter>);
}

{ // InnerBase does not model bidirectional-common
{ // InnerBase does not model bidirectional range
using V = std::ranges::owning_view<std::vector<std::forward_list<int>>>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<V, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!CanPreDecrement<Iter>);
static_assert(!CanPostDecrement<Iter>);
static_assert(!CanPreDecrement<CIter>);
static_assert(!CanPostDecrement<CIter>);
}

{ // InnerBase does not model common range
using InnerBase = BasicVectorView<int, ViewProperties{.common = false}, bidirectional_iterator>;
using V = std::ranges::owning_view<std::vector<InnerBase>>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<V, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!CanPreDecrement<Iter>);
static_assert(!CanPostDecrement<Iter>);
static_assert(!CanPreDecrement<CIter>);
static_assert(!CanPostDecrement<CIter>);
}
}

{ // PatternBase does not model bidirectional-common
{ // PatternBase does not model bidirectional range
using V = std::ranges::owning_view<std::vector<std::vector<int>>>;
using Pattern = std::ranges::owning_view<std::forward_list<int>>;
using JWV = std::ranges::join_with_view<V, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!CanPreDecrement<Iter>);
static_assert(!CanPostDecrement<Iter>);
static_assert(!CanPreDecrement<CIter>);
static_assert(!CanPostDecrement<CIter>);
}

{ // PatternBase does not model common range
using V = std::ranges::owning_view<std::vector<std::vector<int>>>;
using Pattern = BasicVectorView<int, ViewProperties{.common = false}, bidirectional_iterator>;
using JWV = std::ranges::join_with_view<V, Pattern>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!CanPreDecrement<Iter>);
static_assert(!CanPostDecrement<Iter>);
static_assert(!CanPreDecrement<CIter>);
static_assert(!CanPostDecrement<CIter>);
}
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// constexpr decltype(auto) operator*() const;

#include <ranges>

#include <array>
#include <cassert>
#include <string>
#include <string_view>
#include <utility>
#include <vector>

#include "../types.h"

constexpr bool test() {
{ // Result of `operator*` is (maybe const) lvalue reference
using V = std::ranges::owning_view<std::vector<std::string>>;
using Pattern = std::ranges::owning_view<std::string>;
using JWV = std::ranges::join_with_view<V, Pattern>;

JWV jwv(V{{"ab", "cd", "ef"}}, Pattern{"><"});

{
auto it = jwv.begin();
std::same_as<char&> decltype(auto) v_ref = *std::as_const(it);
assert(v_ref == 'a');
std::ranges::advance(it, 2);
std::same_as<char&> decltype(auto) pattern_ref = *it;
assert(pattern_ref == '>');
}

{
auto cit = std::as_const(jwv).begin();
std::same_as<const char&> decltype(auto) cv_ref = *cit;
assert(cv_ref == 'a');
std::ranges::advance(cit, 3);
std::same_as<const char&> decltype(auto) cpattern_ref = *std::as_const(cit);
assert(cpattern_ref == '<');
}
}

{ // Result of `operator*` is const lvalue reference
using V = std::ranges::owning_view<std::vector<std::string_view>>;
using Pattern = std::string_view;
using JWV = std::ranges::join_with_view<V, Pattern>;

JWV jwv(V{{"123", "456", "789"}}, Pattern{"._."});

{
auto it = jwv.begin();
std::same_as<const char&> decltype(auto) v_ref = *it;
assert(v_ref == '1');
std::ranges::advance(it, 3);
std::same_as<const char&> decltype(auto) pattern_ref = *std::as_const(it);
assert(pattern_ref == '.');
}

{
auto cit = std::as_const(jwv).begin();
std::same_as<const char&> decltype(auto) cv_ref = *std::as_const(cit);
assert(cv_ref == '1');
std::ranges::advance(cit, 4);
std::same_as<const char&> decltype(auto) cpattern_ref = *cit;
assert(cpattern_ref == '_');
}
}

{ // Result of `operator*` is prvalue
using V = std::vector<std::string_view>;
using Pattern = RvalueVector<char>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

JWV jwv(V{"x^2", "y^2", "z^2"}, Pattern{{' ', '+', ' '}});

{
auto it = jwv.begin();
std::same_as<char> decltype(auto) v_ref = *std::as_const(it);
assert(v_ref == 'x');
std::ranges::advance(it, 3);
std::same_as<char> decltype(auto) pattern_ref = *it;
assert(pattern_ref == ' ');
}

{
auto cit = std::as_const(jwv).begin();
std::same_as<char> decltype(auto) cv_ref = *cit;
assert(cv_ref == 'x');
std::ranges::advance(cit, 4);
std::same_as<char> decltype(auto) cpattern_ref = *std::as_const(cit);
assert(cpattern_ref == '+');
}
}

{ // Result of `operator*` is (maybe const) rvalue reference
using Inner = std::ranges::as_rvalue_view<std::ranges::owning_view<std::string>>;
using V = std::ranges::owning_view<std::vector<Inner>>;
using Pattern = std::ranges::as_rvalue_view<std::ranges::owning_view<std::array<char, 2>>>;
using JWV = std::ranges::join_with_view<V, Pattern>;

std::vector<Inner> vec;
vec.emplace_back(Inner{{"x*y"}});
vec.emplace_back(Inner{{"y*z"}});
vec.emplace_back(Inner{{"z*x"}});
JWV jwv(V(std::move(vec)), Pattern(std::array{',', ' '}));

{
auto it = jwv.begin();
std::same_as<char&&> decltype(auto) v_ref = *it;
assert(v_ref == 'x');
std::ranges::advance(it, 3);
std::same_as<char&&> decltype(auto) pattern_ref = *std::as_const(it);
assert(pattern_ref == ',');
}

{
auto cit = std::as_const(jwv).begin();
std::same_as<const char&&> decltype(auto) cv_ref = *std::as_const(cit);
assert(cv_ref == 'x');
std::ranges::advance(cit, 4);
std::same_as<const char&&> decltype(auto) cpattern_ref = *cit;
assert(cpattern_ref == ' ');
}
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// friend constexpr bool operator==(const iterator& x, const iterator& y)
// requires ref-is-glvalue && forward_range<Base> &&
// equality_comparable<InnerIter>;

#include <ranges>

#include <array>
#include <cassert>
#include <utility>

#include "../types.h"
#include "test_comparisons.h"

template <class I1, class I2 = I1>
concept CanEq = requires(const I1& i1, const I2& i2) {
{ i1 == i2 } -> std::same_as<bool>;
{ i2 == i1 } -> std::same_as<bool>;
{ i1 != i2 } -> std::same_as<bool>;
{ i2 != i1 } -> std::same_as<bool>;
};

constexpr bool test() {
{ // `V` and `Pattern` are not empty. Test return types too.
using V = std::array<std::array<int, 2>, 3>;
using Pattern = std::array<long, 1>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(CanEq<Iter>);
static_assert(CanEq<CIter>);
static_assert(CanEq<Iter, CIter>);

JWV jwv(V{{{9, 8}, {7, 6}, {5, 4}}}, Pattern{0L});

Iter it1 = jwv.begin();
assert(*it1 == 9);
assert(testEquality(it1, it1, true));

Iter it2 = std::ranges::prev(jwv.end());
assert(*it2 == 4);
assert(testEquality(it2, it2, true));
assert(testEquality(it1, it2, false));

CIter cit1 = std::as_const(jwv).begin();
assert(*cit1 == 9);
assert(testEquality(cit1, cit1, true));
assert(testEquality(it1, cit1, true));
assert(testEquality(it2, cit1, false));

CIter cit2 = std::ranges::prev(std::as_const(jwv).end());
assert(*cit2 == 4);
assert(testEquality(cit2, cit2, true));
assert(testEquality(cit1, cit2, false));
assert(testEquality(it1, cit2, false));
assert(testEquality(it2, cit2, true));

// `it1.inner_it_` and `it2.inner_it_` are equal, but `it1.outer_it_` and `it2.outer_it_` are not.
std::ranges::advance(it1, 2);
assert(*it1 == 0);
std::ranges::advance(it2, -2);
assert(*it2 == 0);
assert(testEquality(it1, it2, false));

// `cit1.inner_it_` and `cit2.inner_it_` are equal, but `cit1.outer_it_` and `cit2.outer_it_` are not.
std::ranges::advance(cit1, 2);
assert(*cit1 == 0);
assert(testEquality(it1, cit1, true));
std::ranges::advance(cit2, -2);
assert(*cit2 == 0);
assert(testEquality(it2, cit2, true));
assert(testEquality(cit1, cit2, false));

// `it1.inner_it_` and `it2.inner_it_` are equal, `it1.outer_it_` and `it2.outer_it_` are equal too.
// `it1.inner_it_index()` and `it2.inner_it_index()` are equal to 1.
++it1;
assert(*it1 == 7);
std::ranges::advance(it2, -2);
assert(*it2 == 7);
assert(testEquality(it1, it2, true));

// `cit1.inner_it_` and `cit2.inner_it_` are equal, `cit1.outer_it_` and `cit2.outer_it_` are equal too.
// `cit1.inner_it_index()` and `cit2.inner_it_index()` are equal to 1.
++cit1;
assert(*cit1 == 7);
assert(testEquality(it1, cit1, true));
std::ranges::advance(cit2, -2);
assert(*cit2 == 7);
assert(testEquality(it2, cit2, true));
assert(testEquality(cit1, cit2, true));

// `it1.inner_it_` and `it2.inner_it_` are equal, `it1.outer_it_` and `it2.outer_it_` are equal too.
// `it1.inner_it_index()` and `it2.inner_it_index()` are equal to 0.
--it1;
assert(*it1 == 0);
--it2;
assert(*it2 == 0);
assert(testEquality(it1, it2, true));

// `cit1.inner_it_` and `cit2.inner_it_` are equal, `cit1.outer_it_` and `cit2.outer_it_` are equal too.
// `cit1.inner_it_index()` and `cit2.inner_it_index()` are equal to 0.
--cit1;
assert(*cit1 == 0);
assert(testEquality(it1, cit1, true));
--cit2;
assert(*cit2 == 0);
assert(testEquality(it2, cit2, true));
assert(testEquality(cit2, cit2, true));
}

{ // `InnerIter` models input iterator and equality comparable. `Pattern` is empty.
using Inner = BasicVectorView<int, ViewProperties{.common = false}, EqComparableInputIter>;
using V = std::vector<Inner>;
using Pattern = std::ranges::empty_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(CanEq<Iter>);
static_assert(CanEq<CIter>);
static_assert(!CanEq<CIter, Iter>);

JWV jwv(V{Inner{1, 2}, Inner{5, 6}, Inner{9, 0}}, Pattern{});

{
Iter it1 = jwv.begin();
assert(*it1 == 1);
Iter it2 = std::ranges::next(jwv.begin(), 2);
assert(*it2 == 5);
assert(testEquality(it1, it2, false));
++it1;
++it1;
assert(testEquality(it1, it2, true));
++it1;
assert(testEquality(it1, it2, false));
}

{
CIter cit1 = std::as_const(jwv).begin();
assert(*cit1 == 1);
CIter cit2 = std::ranges::next(std::as_const(jwv).begin(), 2);
assert(*cit2 == 5);
assert(testEquality(cit1, cit2, false));
++cit1;
++cit1;
assert(testEquality(cit1, cit2, true));
++cit1;
assert(testEquality(cit1, cit2, false));
}
}

{ // `Pattern` is not empty. Some elements of `V` are.
using Inner = BasicVectorView<int, ViewProperties{.common = false}, EqComparableInputIter>;
using V = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
using Pattern = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
using JWV = std::ranges::join_with_view<V, Pattern>;

using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(CanEq<Iter>);
static_assert(CanEq<CIter>);
static_assert(!CanEq<CIter, Iter>);

JWV jwv(V{Inner{1}, Inner{}, Inner{27}}, Pattern{0});

{
Iter it1 = jwv.begin();
assert(*it1 == 1);
++it1;
assert(*it1 == 0);
Iter it2 = jwv.begin();
assert(testEquality(it1, it2, false));
++it2;
assert(testEquality(it1, it2, true));

++it2;
assert(*it1 == *it2);
assert(testEquality(it1, it2, false));

std::ranges::advance(it1, 2);
++it2;
assert(*it1 == *it2);
assert(testEquality(it1, it2, true));
}

{
CIter cit1 = std::as_const(jwv).begin();
assert(*cit1 == 1);
++cit1;
assert(*cit1 == 0);
CIter cit2 = std::as_const(jwv).begin();
assert(testEquality(cit1, cit2, false));
++cit2;
assert(testEquality(cit1, cit2, true));

++cit2;
assert(*cit1 == *cit2);
assert(testEquality(cit1, cit2, false));

std::ranges::advance(cit1, 2);
++cit2;
assert(*cit1 == *cit2);
assert(testEquality(cit1, cit2, true));
}
}

{ // `ref-is-glvalue` is false
using Inner = std::vector<int>;
using V = RvalueVector<Inner>;
using Pattern = std::ranges::empty_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
using Iter = std::ranges::iterator_t<JWV>;
static_assert(!CanEq<Iter>);
}

{ // `Base` does not model forward range
using Inner = std::vector<int>;
using V = BasicVectorView<Inner, ViewProperties{}, DefaultCtorInputIter>;
using Pattern = std::ranges::empty_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
using Iter = std::ranges::iterator_t<JWV>;
static_assert(!CanEq<Iter>);
}

{ // `InnerIter` does not model equality comparable
using Inner = BasicVectorView<int, ViewProperties{.common = false}, cpp20_input_iterator>;
using V = std::vector<Inner>;
using Pattern = std::ranges::empty_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(!CanEq<Iter>);
static_assert(!CanEq<CIter>);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,320 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// constexpr iterator& operator++();
// constexpr void operator++(int);
// constexpr iterator operator++(int)
// requires ref-is-glvalue && forward_iterator<OuterIter> &&
// forward_iterator<InnerIter>;

#include <ranges>

#include <array>
#include <cassert>
#include <type_traits>
#include <vector>

#include "../types.h"

template <class I>
concept CanPreIncrement = requires(I& i) { ++i; };

template <class I>
concept CanPostIncrement = requires(I& i) { i++; };

constexpr void test_pre_increment() {
{ // `V` and `Pattern` are not empty. Test return type too.
using V = std::array<std::array<int, 2>, 3>;
using Pattern = std::array<int, 2>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(CanPreIncrement<Iter>);
static_assert(!CanPreIncrement<const Iter>);
static_assert(CanPreIncrement<CIter>);
static_assert(!CanPreIncrement<const CIter>);

JWV jwv(V{{{1, 1}, {2, 2}, {3, 3}}}, Pattern{0, 0});

{
auto it = jwv.begin();
assert(*it == 1);
std::same_as<Iter&> decltype(auto) it_ref = ++it;
assert(it_ref == it);
++it;
assert(*it == 0);
++it_ref;
++it_ref;
assert(*it_ref == 2);
++it;
++it_ref;
assert(*it == 0);
}

{
auto cit = std::as_const(jwv).begin();
assert(*cit == 1);
std::same_as<CIter&> decltype(auto) cit_ref = ++cit;
assert(cit_ref == cit);
++cit;
assert(*cit == 0);
++cit_ref;
++cit_ref;
assert(*cit_ref == 2);
++cit;
++cit_ref;
assert(*cit == 0);
}
}

#if !defined(TEST_COMPILER_GCC) // GCC c++/101777

{ // `Pattern` is empty, `V` is not.
using V = std::array<std::vector<int>, 3>;
using Pattern = std::vector<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

JWV jwv(V{{{-1}, {-2}, {-3}}}, Pattern{});

{
auto it = jwv.begin();
assert(*it == -1);
++it;
assert(*it == -2);
++it;
assert(*it == -3);
++it;
assert(it == jwv.end());
}

{
auto cit = std::as_const(jwv).begin();
assert(*cit == -1);
++cit;
assert(*cit == -2);
++cit;
assert(*cit == -3);
++cit;
assert(cit == std::as_const(jwv).end());
}
}

{ // `V` has empty subrange in the middle, `Pattern` is not empty.
using V = std::array<std::vector<int>, 3>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;

JWV jwv(V{{{1}, {}, {3}}}, Pattern{0});

{
auto it = jwv.begin();
assert(*it == 1);
++it;
assert(*it == 0);
++it;
assert(*it == 0);
++it;
assert(*it == 3);
}

{
auto cit = std::as_const(jwv).begin();
assert(*cit == 1);
++cit;
assert(*cit == 0);
++cit;
assert(*cit == 0);
++cit;
assert(*cit == 3);
}
}

{ // Only first element of `V` is not empty. `Pattern` is empty.
using V = std::array<std::vector<int>, 3>;
using Pattern = std::ranges::empty_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;

JWV jwv(V{{{777}, {}, {}}}, Pattern{});

{
auto it = jwv.begin();
assert(*it == 777);
++it;
assert(it == jwv.end());
}

{
auto cit = std::as_const(jwv).begin();
assert(*cit == 777);
++cit;
assert(cit == std::as_const(jwv).end());
}
}

#endif // !defined(TEST_COMPILER_GCC)

{ // Only last element of `V` is not empty. `Pattern` is empty. `V` models input range and `ref-is-glvalue` is false.
using Inner = RvalueVector<std::string>;
using V = BasicView<Inner, ViewProperties{}, DefaultCtorInputIter>;
using Pattern = std::ranges::empty_view<char>;
using JWV = std::ranges::join_with_view<V, Pattern>;

JWV jwv(V{{}, {}, {'a'}}, Pattern{});

auto it = jwv.begin();
assert(*it == 'a');
++it;
assert(it == jwv.end());
}

{ // Only first element of `V` is not empty. `Pattern` is not empty.
// `V` models input range and `ref-is-glvalue` is false.
using Inner = RvalueVector<std::string>;
using V = BasicView<Inner, ViewProperties{}, DefaultCtorInputIter>;
using Pattern = std::ranges::single_view<char>;
using JWV = std::ranges::join_with_view<V, Pattern>;

JWV jwv(V{{'b'}, {}, {}}, Pattern{'.'});

auto it = jwv.begin();
assert(*it == 'b');
++it;
assert(*it == '.');
++it;
assert(*it == '.');
++it;
assert(it == jwv.end());
}
}

constexpr void test_post_increment() {
{ // `V` and `Pattern` are not empty. Test return type too.
using V = std::array<std::array<int, 3>, 2>;
using Pattern = std::array<int, 1>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

using Iter = std::ranges::iterator_t<JWV>;
using CIter = std::ranges::iterator_t<const JWV>;
static_assert(CanPostIncrement<Iter>);
static_assert(!CanPostIncrement<const Iter>);
static_assert(CanPostIncrement<CIter>);
static_assert(!CanPostIncrement<const CIter>);

JWV jwv(V{{{6, 5, 4}, {3, 2, 1}}}, Pattern{-5});

{
auto it = jwv.begin();
assert(*it == 6);
std::same_as<Iter> decltype(auto) it_copy = it++;
assert(++it_copy == it);
it++;
it++;
assert(*it == -5);
it_copy++;
it_copy++;
assert(*it_copy == -5);
it++;
it_copy++;
assert(*it == 3);
assert(*it_copy == 3);
}

{
auto cit = std::as_const(jwv).begin();
assert(*cit == 6);
std::same_as<CIter> decltype(auto) cit_copy = cit++;
assert(++cit_copy == cit);
cit++;
cit++;
assert(*cit == -5);
cit_copy++;
cit_copy++;
assert(*cit_copy == -5);
cit++;
cit_copy++;
assert(*cit == 3);
assert(*cit_copy == 3);
}
}

{ // `Pattern` is empty, `V` is not. `ref-is-glvalue` is false.
using Inner = std::vector<int>;
using V = RvalueVector<Inner>;
using Pattern = std::ranges::empty_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

JWV jwv(V{Inner{-3}, Inner{-2}, Inner{-1}}, Pattern{});

auto it = jwv.begin();
assert(*it == -3);
it++;
assert(*it == -2);
it++;
assert(*it == -1);
it++;
assert(it == jwv.end());
static_assert(std::is_void_v<decltype(it++)>);
}

{ // `V` has empty subrange in the middle, `Pattern` is not empty. OuterIter does not model forward iterator.
using Inner = std::vector<int>;
using V = BasicVectorView<Inner, ViewProperties{.common = false}, cpp20_input_iterator>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<V, Pattern>;

JWV jwv(V{Inner{7}, {}, Inner{9}}, Pattern{8});

auto it = jwv.begin();
assert(*it == 7);
it++;
assert(*it == 8);
it++;
assert(*it == 8);
it++;
assert(*it == 9);
it++;
assert(it == jwv.end());
static_assert(std::is_void_v<decltype(it++)>);
}

#if !defined(TEST_COMPILER_GCC) // GCC c++/101777
{ // Only first element of `V` is not empty. `Pattern` is empty. InnerIter does not model forward iterator.
using Inner = BasicVectorView<char32_t, ViewProperties{.common = false}, cpp17_input_iterator>;
using V = std::array<Inner, 3>;
using Pattern = std::ranges::empty_view<char32_t>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;

JWV jwv(V{Inner{U'?'}, Inner{}, Inner{}}, Pattern{});

auto it = jwv.begin();
assert(*it == U'?');
it++;
assert(it == jwv.end());
static_assert(std::is_void_v<decltype(it++)>);
}
#endif // !defined(TEST_COMPILER_GCC)
}

constexpr bool test() {
test_pre_increment();
test_post_increment();

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,340 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// friend constexpr decltype(auto) iter_move(const iterator& x) {
// using rvalue_reference = common_reference_t<
// iter_rvalue_reference_t<InnerIter>,
// iter_rvalue_reference_t<PatternIter>>;
// return visit<rvalue_reference>(ranges::iter_move, x.inner_it_);
// }

#include <ranges>

#include <algorithm>
#include <array>
#include <cassert>
#include <type_traits>
#include <utility>
#include <vector>

#include "../types.h"

class MoveOnlyInt {
public:
enum Status { constructed, move_constructed, moved_from_this };

MoveOnlyInt() = default;
constexpr MoveOnlyInt(int val) : val_(val) {}

constexpr MoveOnlyInt(MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) {
other.val_ = -1;
other.status_ = moved_from_this;
}

constexpr MoveOnlyInt(const MoveOnlyInt&& other) noexcept : val_(other.val_), status_(move_constructed) {
other.val_ = -1;
other.status_ = moved_from_this;
}

MoveOnlyInt(const MoveOnlyInt&) { assert(false); } // Should never be called in this test.

MoveOnlyInt& operator=(MoveOnlyInt&&) { // Should never be called in this test.
assert(false);
return *this;
}

constexpr Status get_status() const { return status_; }

friend constexpr bool operator==(const MoveOnlyInt& left, int right) { return left.val_ == right; }
friend constexpr bool operator==(const MoveOnlyInt& left, const MoveOnlyInt& right) {
return left.val_ == right.val_;
}

private:
mutable int val_ = -1;
mutable Status status_ = constructed;
};

static_assert(std::movable<MoveOnlyInt>);

template <class T>
class ProxyRvalue {
T val_;

public:
constexpr ProxyRvalue(T val) : val_(std::move(val)) {}

ProxyRvalue(ProxyRvalue&&) = default;
ProxyRvalue& operator=(ProxyRvalue&&) = default;

constexpr explicit operator T&&() noexcept { return std::move(val_); }
};

static_assert(std::common_reference_with<ProxyRvalue<int>, int>);
static_assert(std::common_reference_with<ProxyRvalue<MoveOnlyInt>, MoveOnlyInt>);

template <std::bidirectional_iterator It>
class ProxyOnIterMoveIter {
It it_ = It();

public:
using value_type = std::iter_value_t<It>;
using difference_type = std::iter_difference_t<It>;

ProxyOnIterMoveIter() = default;
constexpr ProxyOnIterMoveIter(It it) : it_(std::move(it)) {}

constexpr decltype(auto) operator*() const { return *it_; }

constexpr ProxyOnIterMoveIter& operator++() {
++it_;
return *this;
}

constexpr ProxyOnIterMoveIter operator++(int) {
ProxyOnIterMoveIter copy = *this;
++it_;
return copy;
}

constexpr ProxyOnIterMoveIter& operator--() {
--it_;
return *this;
}

constexpr ProxyOnIterMoveIter operator--(int) {
ProxyOnIterMoveIter copy = *this;
--it_;
return copy;
}

friend bool operator==(const ProxyOnIterMoveIter&, const ProxyOnIterMoveIter&) = default;

friend constexpr ProxyRvalue<value_type> iter_move(const ProxyOnIterMoveIter iter) {
return ProxyRvalue<value_type>{std::ranges::iter_move(iter.it_)};
}
};

template <class It>
ProxyOnIterMoveIter(It) -> ProxyOnIterMoveIter<It>;

static_assert(std::bidirectional_iterator<ProxyOnIterMoveIter<int*>>);

constexpr bool test() {
{ // Test `iter_move` when result is true rvalue reference. Test return types.
using V = std::array<std::array<char, 1>, 2>;
using Pattern = std::array<char, 1>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

JWV jwv(V{{{'0'}, {'1'}}}, Pattern{','});

{
auto it = jwv.begin();
std::same_as<char&&> decltype(auto) v_rref1 = iter_move(it);
std::same_as<char&&> decltype(auto) v_rref2 = iter_move(std::as_const(it));
std::same_as<char&&> decltype(auto) v_rref3 = std::ranges::iter_move(it);
std::same_as<char&&> decltype(auto) v_rref4 = std::ranges::iter_move(std::as_const(it));
assert(std::ranges::equal(std::array{v_rref1, v_rref2, v_rref3, v_rref4}, std::views::repeat('0', 4)));

++it; // `it` points to element of `Pattern` from here
std::same_as<char&&> decltype(auto) pattern_rref1 = iter_move(it);
std::same_as<char&&> decltype(auto) pattern_rref2 = iter_move(std::as_const(it));
std::same_as<char&&> decltype(auto) pattern_rref3 = std::ranges::iter_move(it);
std::same_as<char&&> decltype(auto) pattern_rref4 = std::ranges::iter_move(std::as_const(it));
assert(std::ranges::equal(
std::array{pattern_rref1, pattern_rref2, pattern_rref3, pattern_rref4}, std::views::repeat(',', 4)));
}

{
auto cit = std::prev(std::as_const(jwv).end());
std::same_as<const char&&> decltype(auto) cv_rref1 = iter_move(cit);
std::same_as<const char&&> decltype(auto) cv_rref2 = iter_move(std::as_const(cit));
std::same_as<const char&&> decltype(auto) cv_rref3 = std::ranges::iter_move(cit);
std::same_as<const char&&> decltype(auto) cv_rref4 = std::ranges::iter_move(std::as_const(cit));
assert(std::ranges::equal(std::array{cv_rref1, cv_rref2, cv_rref3, cv_rref4}, std::views::repeat('1', 4)));

cit--; // `cit` points to element of `Pattern` from here
std::same_as<const char&&> decltype(auto) cpattern_rref1 = iter_move(cit);
std::same_as<const char&&> decltype(auto) cpattern_rref2 = iter_move(std::as_const(cit));
std::same_as<const char&&> decltype(auto) cpattern_rref3 = std::ranges::iter_move(cit);
std::same_as<const char&&> decltype(auto) cpattern_rref4 = std::ranges::iter_move(std::as_const(cit));
assert(std::ranges::equal(
std::array{cpattern_rref1, cpattern_rref2, cpattern_rref3, cpattern_rref4}, std::views::repeat(',', 4)));
}
}

{ // Test `iter_move` when result is true rvalue reference. Test moving.
using Inner = std::vector<MoveOnlyInt>;
using V = std::vector<Inner>;
using Pattern = std::vector<MoveOnlyInt>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

V v;
v.reserve(2);
v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
JWV jwv(std::move(v), std::ranges::to<Pattern>(std::views::iota(4, 12)));
assert(std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::constructed; }));

{
using enum MoveOnlyInt::Status;
std::vector<MoveOnlyInt> values;
values.reserve(8);

auto it = jwv.begin();
values.emplace_back(iter_move(it));
++it;
values.emplace_back(iter_move(std::as_const(it)));
it++;
values.emplace_back(std::ranges::iter_move(it));
++it;
values.emplace_back(std::ranges::iter_move(std::as_const(it)));
it++; // `it` points to element of `Pattern` from here
values.emplace_back(iter_move(it));
++it;
values.emplace_back(iter_move(std::as_const(it)));
it++;
values.emplace_back(std::ranges::iter_move(it));
++it;
values.emplace_back(std::ranges::iter_move(std::as_const(it)));

assert(std::ranges::equal(values, std::views::iota(0, 8)));
assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
}

{
using enum MoveOnlyInt::Status;
std::vector<MoveOnlyInt> values;
values.reserve(8);

auto cit = std::prev(std::as_const(jwv).end());
values.emplace_back(iter_move(cit));
cit--;
values.emplace_back(iter_move(std::as_const(cit)));
--cit;
values.emplace_back(std::ranges::iter_move(cit));
cit--;
values.emplace_back(std::ranges::iter_move(std::as_const(cit)));
--cit; // `it` points to element of `Pattern` from here
values.emplace_back(iter_move(cit));
cit--;
values.emplace_back(iter_move(std::as_const(cit)));
--cit;
values.emplace_back(std::ranges::iter_move(cit));
cit--;
values.emplace_back(std::ranges::iter_move(std::as_const(cit)));

assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
}

assert(
std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::moved_from_this; }));
}

{ // Test `iter_move` when result is proxy rvalue reference. Test return types and moving.
using Inner = std::vector<MoveOnlyInt>;
using V = std::vector<Inner>;
using Pattern = BasicVectorView<MoveOnlyInt, ViewProperties{}, ProxyOnIterMoveIter>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;

using RRef = ProxyRvalue<MoveOnlyInt>;
static_assert(std::same_as<RRef, std::ranges::range_rvalue_reference_t<JWV>>);

V v;
v.reserve(2);
v.emplace_back(std::ranges::to<Inner>(std::views::iota(0, 4)));
v.emplace_back(std::ranges::to<Inner>(std::views::iota(12, 16)));
JWV jwv(std::move(v), Pattern{std::ranges::to<std::vector<MoveOnlyInt>>(std::views::iota(4, 12))});
assert(std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::constructed; }));

{
using enum MoveOnlyInt::Status;
std::vector<MoveOnlyInt> values;
values.reserve(8);

auto it = jwv.begin();
std::same_as<RRef> decltype(auto) rref1 = iter_move(it);
values.emplace_back(std::move(rref1));
++it;
std::same_as<RRef> decltype(auto) rref2 = iter_move(std::as_const(it));
values.emplace_back(rref2);
it++;
std::same_as<RRef> decltype(auto) rref3 = std::ranges::iter_move(it);
values.emplace_back(rref3);
++it;
std::same_as<RRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(it));
values.emplace_back(rref4);
it++; // `it` points to element of `Pattern` from here
std::same_as<RRef> decltype(auto) rref5 = iter_move(it);
values.emplace_back(rref5);
++it;
std::same_as<RRef> decltype(auto) rref6 = iter_move(std::as_const(it));
values.emplace_back(rref6);
it++;
std::same_as<RRef> decltype(auto) rref7 = std::ranges::iter_move(it);
values.emplace_back(rref7);
++it;
std::same_as<RRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(it));
values.emplace_back(rref8);

assert(std::ranges::equal(values, std::views::iota(0, 8)));
assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
}

{
using enum MoveOnlyInt::Status;
std::vector<MoveOnlyInt> values;
values.reserve(8);

auto cit = std::prev(std::as_const(jwv).end());
std::same_as<RRef> decltype(auto) rref1 = iter_move(cit);
values.emplace_back(rref1);
cit--;
std::same_as<RRef> decltype(auto) rref2 = iter_move(std::as_const(cit));
values.emplace_back(rref2);
--cit;
std::same_as<RRef> decltype(auto) rref3 = std::ranges::iter_move(cit);
values.emplace_back(rref3);
cit--;
std::same_as<RRef> decltype(auto) rref4 = std::ranges::iter_move(std::as_const(cit));
values.emplace_back(rref4);
--cit; // `it` points to element of `Pattern` from here
std::same_as<RRef> decltype(auto) rref5 = iter_move(cit);
values.emplace_back(rref5);
cit--;
std::same_as<RRef> decltype(auto) rref6 = iter_move(std::as_const(cit));
values.emplace_back(rref6);
--cit;
std::same_as<RRef> decltype(auto) rref7 = std::ranges::iter_move(cit);
values.emplace_back(rref7);
cit--;
std::same_as<RRef> decltype(auto) rref8 = std::ranges::iter_move(std::as_const(cit));
values.emplace_back(rref8);

assert(std::ranges::equal(std::views::reverse(values), std::views::iota(8, 16)));
assert(std::ranges::all_of(values, [](const MoveOnlyInt& i) { return i.get_status() == move_constructed; }));
}

assert(
std::ranges::all_of(jwv, [](const MoveOnlyInt& i) { return i.get_status() == MoveOnlyInt::moved_from_this; }));
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// friend constexpr void iter_swap(const iterator& x, const iterator& y)
// requires indirectly_swappable<InnerIter, PatternIter> {
// visit(ranges::iter_swap, x.inner_it_, y.inner_it_);
// }

#include <ranges>

#include <algorithm>
#include <cassert>
#include <span>
#include <string>
#include <string_view>
#include <type_traits>
#include <utility>
#include <vector>

template <class I>
concept CanIterSwap = requires(I i) { iter_swap(i); };

constexpr bool test() {
{ // Test common usage
using V = std::vector<std::string>;
using Pattern = std::string;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, std::ranges::owning_view<Pattern>>;
using namespace std::string_view_literals;

JWV jwv(V{"std", "ranges", "views", "join_with_view"}, Pattern{":: "});
assert(std::ranges::equal(jwv, "std:: ranges:: views:: join_with_view"sv));

auto it = jwv.begin();
iter_swap(it, std::ranges::next(it, 2)); // Swap elements of the same inner range.
assert(std::ranges::equal(jwv, "dts:: ranges:: views:: join_with_view"sv));

std::ranges::advance(it, 3);
iter_swap(std::as_const(it), std::ranges::next(it, 2)); // Swap elements of the pattern.
assert(std::ranges::equal(jwv, "dts ::ranges ::views ::join_with_view"sv));

std::ranges::advance(it, 3);
const auto it2 = jwv.begin();
iter_swap(std::as_const(it), it2); // Swap elements of different inner ranges.
assert(std::ranges::equal(jwv, "rts ::danges ::views ::join_with_view"sv));

std::ranges::advance(it, 6);
iter_swap(std::as_const(it), it2); // Swap element from inner range with element from the pattern.
assert(std::ranges::equal(jwv, " tsr::dangesr::viewsr::join_with_view"sv));

static_assert(std::is_void_v<decltype(iter_swap(it, it))>);
static_assert(std::is_void_v<decltype(iter_swap(it2, it2))>);
static_assert(!CanIterSwap<std::ranges::iterator_t<const JWV>>);
static_assert(!CanIterSwap<const std::ranges::iterator_t<const JWV>>);
}

{ // InnerIter and PatternIter don't model indirectly swappable
using JWV = std::ranges::join_with_view<std::span<std::string>, std::string_view>;
static_assert(!CanIterSwap<std::ranges::iterator_t<JWV>>);
static_assert(!CanIterSwap<const std::ranges::iterator_t<JWV>>);
static_assert(!CanIterSwap<std::ranges::iterator_t<const JWV>>);
static_assert(!CanIterSwap<const std::ranges::iterator_t<const JWV>>);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,359 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// std::views::join_with_view

#include <ranges>

#include <span>
#include <string_view>
#include <utility>

#include "test_iterators.h"

template <class View, class T>
concept CanBePiped = requires(View&& view, T&& t) {
{ std::forward<View>(view) | std::forward<T>(t) };
};

struct Range : std::ranges::view_base {
using Iterator = forward_iterator<std::string_view*>;
using Sentinel = sentinel_wrapper<Iterator>;
constexpr explicit Range(std::string_view* b, std::string_view* e) : begin_(b), end_(e) {}
constexpr Iterator begin() const { return Iterator(begin_); }
constexpr Sentinel end() const { return Sentinel(Iterator(end_)); }

private:
std::string_view* begin_;
std::string_view* end_;
};

struct Pattern : std::ranges::view_base {
using Iterator = forward_iterator<const char*>;
using Sentinel = sentinel_wrapper<Iterator>;
static constexpr std::string_view pat{", "};

constexpr Pattern() = default;
constexpr Iterator begin() const { return Iterator(pat.data()); }
constexpr Sentinel end() const { return Sentinel(Iterator(pat.data() + pat.size())); }
};

struct NonCopyablePattern : Pattern {
NonCopyablePattern(const NonCopyablePattern&) = delete;
};

template <typename View>
constexpr void compareViews(View v, std::string_view list) {
auto b1 = v.begin();
auto e1 = v.end();
auto b2 = list.begin();
auto e2 = list.end();
for (; b1 != e1 && b2 != e2; ++b1, ++b2) {
assert(*b1 == *b2);
}
assert(b1 == e1);
assert(b2 == e2);
}

constexpr int absoluteValue(int x) { return x < 0 ? -x : x; }

template <class T>
constexpr const T&& asConstRvalue(T&& t) {
return static_cast<const T&&>(t);
}

constexpr void test_adaptor_with_pattern(std::span<std::string_view> buff) {
// Test `views::join_with(pattern)(v)`
{
using Result = std::ranges::join_with_view<Range, Pattern>;
const Range range(buff.data(), buff.data() + buff.size());
Pattern pattern;

{
// 'views::join_with(pattern)' - &&
std::same_as<Result> decltype(auto) result = std::views::join_with(pattern)(range);
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with(pattern)' - const&&
std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(pattern))(range);
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with(pattern)' - &
auto partial = std::views::join_with(pattern);
std::same_as<Result> decltype(auto) result = partial(range);
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with(pattern)' - const&
auto const partial = std::views::join_with(pattern);
std::same_as<Result> decltype(auto) result = partial(range);
compareViews(result, "abcd, ef, ghij, kl");
}
}

// Test `v | views::join_with(pattern)`
{
using Result = std::ranges::join_with_view<Range, Pattern>;
const Range range(buff.data(), buff.data() + buff.size());
Pattern pattern;

{
// 'views::join_with(pattern)' - &&
std::same_as<Result> decltype(auto) result = range | std::views::join_with(pattern);
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with(pattern)' - const&&
std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(pattern));
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with(pattern)' - &
auto partial = std::views::join_with(pattern);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with(pattern)' - const&
auto const partial = std::views::join_with(pattern);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, "abcd, ef, ghij, kl");
}
}

// Test `views::join_with(v, pattern)` range adaptor object
{
using Result = std::ranges::join_with_view<Range, Pattern>;
const Range range(buff.data(), buff.data() + buff.size());
Pattern pattern;

{
// 'views::join_with' - &&
auto range_adaptor = std::views::join_with;
std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with' - const&&
const auto range_adaptor = std::views::join_with;
std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, pattern);
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with' - &
auto range_adaptor = std::views::join_with;
std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);
compareViews(result, "abcd, ef, ghij, kl");
}
{
// 'views::join_with' - const&
const auto range_adaptor = std::views::join_with;
std::same_as<Result> decltype(auto) result = range_adaptor(range, pattern);
compareViews(result, "abcd, ef, ghij, kl");
}
}

// Test `adaptor | views::join_with(pattern)`
{
auto pred = [](std::string_view s) { return s.size() >= 3; };
using Result = std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, Pattern>;
const Range range(buff.data(), buff.data() + buff.size());
Pattern pattern;

{
std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(pattern);
compareViews(result, "abcd, ghij");
}
{
const auto partial = std::views::filter(pred) | std::views::join_with(pattern);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, "abcd, ghij");
}
}
}

constexpr void test_adaptor_with_single_element(std::span<std::string_view> buff) {
// Test `views::join_with(element)(v)`
{
using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
const Range range(buff.data(), buff.data() + buff.size());
const char element = '.';

{
// 'views::join_with(element)' - &&
std::same_as<Result> decltype(auto) result = std::views::join_with(element)(range);
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with(element)' - const&&
std::same_as<Result> decltype(auto) result = asConstRvalue(std::views::join_with(element))(range);
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with(element)' - &
auto partial = std::views::join_with(element);
std::same_as<Result> decltype(auto) result = partial(range);
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with(element)' - const&
const auto partial = std::views::join_with(element);
std::same_as<Result> decltype(auto) result = partial(range);
compareViews(result, "abcd.ef.ghij.kl");
}
}

// Test `v | views::join_with(element)`
{
using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
const Range range(buff.data(), buff.data() + buff.size());
const char element = '.';

{
// 'views::join_with(element)' - &&
std::same_as<Result> decltype(auto) result = range | std::views::join_with(element);
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with(element)' - const&&
std::same_as<Result> decltype(auto) result = range | asConstRvalue(std::views::join_with(element));
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with(element)' - &
auto partial = std::views::join_with(element);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with(element)' - const&
const auto partial = std::views::join_with(element);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, "abcd.ef.ghij.kl");
}
}

// Test `views::join_with(v, element)` range adaptor object
{
using Result = std::ranges::join_with_view<Range, std::ranges::single_view<char>>;
const Range range(buff.data(), buff.data() + buff.size());
const char element = '.';

{
// 'views::join_with' - &&
auto range_adaptor = std::views::join_with;
std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with' - const&&
const auto range_adaptor = std::views::join_with;
std::same_as<Result> decltype(auto) result = std::move(range_adaptor)(range, element);
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with' - &
auto range_adaptor = std::views::join_with;
std::same_as<Result> decltype(auto) result = range_adaptor(range, element);
compareViews(result, "abcd.ef.ghij.kl");
}
{
// 'views::join_with' - const&
const auto range_adaptor = std::views::join_with;
std::same_as<Result> decltype(auto) result = range_adaptor(range, element);
compareViews(result, "abcd.ef.ghij.kl");
}
}

// Test `adaptor | views::join_with(element)`
{
auto pred = [](std::string_view s) { return s.size() >= 3; };
using Result =
std::ranges::join_with_view<std::ranges::filter_view<Range, decltype(pred)>, std::ranges::single_view<char>>;
const Range range(buff.data(), buff.data() + buff.size());
const char element = '.';

{
std::same_as<Result> decltype(auto) result = range | std::views::filter(pred) | std::views::join_with(element);
compareViews(result, "abcd.ghij");
}
{
const auto partial = std::views::filter(pred) | std::views::join_with(element);
std::same_as<Result> decltype(auto) result = range | partial;
compareViews(result, "abcd.ghij");
}
}
}

constexpr bool test() {
std::string_view buff[] = {"abcd", "ef", "ghij", "kl"};

// Test range adaptor object
{
using RangeAdaptorObject = decltype(std::views::join_with);
static_assert(std::is_const_v<RangeAdaptorObject>);

// The type of a customization point object, ignoring cv-qualifiers, shall model semiregular
static_assert(std::semiregular<std::remove_const<RangeAdaptorObject>>);
}

test_adaptor_with_pattern(buff);
test_adaptor_with_single_element(buff);

// Test that one can call std::views::join_with with arbitrary stuff, as long as we
// don't try to actually complete the call by passing it a range.
//
// That makes no sense and we can't do anything with the result, but it's valid.
{
long array[3] = {1, 2, 3};
[[maybe_unused]] auto partial = std::views::join_with(std::move(array));
}

// Test SFINAE friendliness
{
struct NotAView {};

static_assert(!CanBePiped<Range, decltype(std::views::join_with)>);
static_assert(CanBePiped<Range, decltype(std::views::join_with(Pattern{}))>);
static_assert(CanBePiped<Range, decltype(std::views::join_with('.'))>);
static_assert(!CanBePiped<NotAView, decltype(std::views::join_with(Pattern{}))>);
static_assert(!CanBePiped<NotAView, decltype(std::views::join_with('.'))>);
static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with(Pattern{}))>);
static_assert(!CanBePiped<std::initializer_list<char>, decltype(std::views::join_with('.'))>);
static_assert(!CanBePiped<Range, decltype(std::views::join_with(NotAView{}))>);

static_assert(!std::is_invocable_v<decltype(std::views::join_with)>);
static_assert(!std::is_invocable_v<decltype(std::views::join_with), Pattern, Range>);
static_assert(!std::is_invocable_v<decltype(std::views::join_with), char, Range>);
static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, Pattern>);
static_assert(std::is_invocable_v<decltype(std::views::join_with), Range, char>);
static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, Pattern, Pattern>);
static_assert(!std::is_invocable_v<decltype(std::views::join_with), Range, char, char>);
static_assert(!std::is_invocable_v<decltype(std::views::join_with), NonCopyablePattern>);
}

{
static_assert(std::is_same_v<decltype(std::ranges::views::join_with), decltype(std::views::join_with)>);
assert(std::addressof(std::ranges::views::join_with) == std::addressof(std::views::join_with));
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// [Example 1:
// vector<string> vs = {"the", "quick", "brown", "fox"};
// for (char c : vs | views::join_with('-')) {
// cout << c;
// }
// // The above prints the-quick-brown-fox
// - end example]

#include <ranges>

#include <algorithm>
#include <string>
#include <string_view>
#include <vector>

using namespace std::string_view_literals;

constexpr bool test() {
std::vector<std::string> vs = {"the", "quick", "brown", "fox"};
std::string result;
for (char c : vs | std::views::join_with('-')) {
result += c;
}

return std::ranges::equal(result, "the-quick-brown-fox"sv);
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// sentinel() = default;

#include <ranges>

#include "../types.h"

constexpr bool test() {
using Inner = BasicVectorView<char, ViewProperties{.common = false}, forward_iterator>;
using V = BasicVectorView<Inner, ViewProperties{}, forward_iterator>;
using Pattern = Inner;
using JWV = std::ranges::join_with_view<V, Pattern>;
static_assert(!std::ranges::common_range<JWV>);

[[maybe_unused]] std::ranges::sentinel_t<JWV> se;
[[maybe_unused]] std::ranges::sentinel_t<const JWV> cse;

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// constexpr sentinel(sentinel<!Const> s)
// requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;

#include <ranges>

#include <type_traits>
#include <vector>

#include "../types.h"
#include "test_iterators.h"

constexpr bool test() {
{ // Regular conversion from `!Const` to `Const` sentinel
using Inner = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
std::vector<Inner> vec = {Inner{11, 12}, Inner{13, 14}};

std::ranges::join_with_view jwv(vec, 0);
using JWV = decltype(jwv);
static_assert(!std::ranges::common_range<JWV>);
using CSent = std::ranges::sentinel_t<const JWV>;

auto se = jwv.end();
[[maybe_unused]] CSent cse = se;
}

{ // Test conversion from `Const` to `!Const` (should be invalid)
using Inner = BasicVectorView<int, ViewProperties{.common = false}, forward_iterator>;
using V = std::vector<Inner>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<std::views::all_t<V>, Pattern>;
static_assert(!std::ranges::common_range<JWV>);

using Sent = std::ranges::sentinel_t<JWV>;
using CSent = std::ranges::sentinel_t<const JWV>;
static_assert(!std::convertible_to<CSent, Sent>);
static_assert(!std::constructible_from<Sent, CSent>);
}

{ // When `convertible_to<sentinel_t<V>, sentinel_t<Base>>` is not modeled
using V = ConstOppositeView<std::vector<long>>;
using Pattern = std::ranges::single_view<long>;
using JWV = std::ranges::join_with_view<V, Pattern>;
static_assert(!std::ranges::common_range<JWV>);

using Sent = std::ranges::sentinel_t<JWV>;
using CSent = std::ranges::sentinel_t<const JWV>;
static_assert(!std::convertible_to<CSent, Sent>);
static_assert(!std::constructible_from<Sent, CSent>);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//===----------------------------------------------------------------------===//
//
// 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, c++20

// <ranges>

// template<bool OtherConst>
// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);

#include <ranges>

#include <cassert>
#include <type_traits>
#include <utility>
#include <vector>

#include "../types.h"
#include "test_comparisons.h"
#include "test_iterators.h"

struct NonCrossConstComparableView : std::ranges::view_base {
using NonConstRange = std::vector<int>;
NonConstRange* begin();
sentinel_wrapper<NonConstRange*> end();

using ConstRange = BasicVectorView<int, ViewProperties{}, forward_iterator>;
ConstRange* begin() const;
sentinel_wrapper<ConstRange*> end() const;
};

static_assert(std::ranges::range<NonCrossConstComparableView>);
static_assert(std::ranges::range<const NonCrossConstComparableView>);

constexpr bool test() {
using Inner = BasicVectorView<int, ViewProperties{.common = false}, cpp20_input_iterator>;
using V = std::vector<Inner>;
using Pattern = std::ranges::single_view<int>;
using JWV = std::ranges::join_with_view<std::ranges::owning_view<V>, Pattern>;
static_assert(!std::ranges::common_range<JWV>);

{ // Compare iterator<Const> with sentinel<Const>
{ // Const == true
AssertEqualityReturnBool<std::ranges::iterator_t<const JWV>, std::ranges::sentinel_t<const JWV>>();
const JWV jwv(V{Inner{1, 2}, Inner{4}}, 3);
assert(testEquality(std::ranges::next(jwv.begin(), 4), jwv.end(), true));
assert(testEquality(jwv.begin(), jwv.end(), false));
}

{ // Const == false
AssertEqualityReturnBool<std::ranges::iterator_t<JWV>, std::ranges::sentinel_t<JWV>>();
JWV jwv(V{Inner{5}, Inner{7, 8}}, 6);
assert(testEquality(std::ranges::next(jwv.begin(), 4), jwv.end(), true));
assert(testEquality(std::ranges::next(jwv.begin(), 2), jwv.end(), false));
}
}

{ // Compare iterator<Const> with sentinel<!Const>
{ // Const == true
AssertEqualityReturnBool<std::ranges::iterator_t<const JWV>, std::ranges::sentinel_t<JWV>>();
JWV jwv(V{Inner{9, 10}, Inner{12}}, 11);
assert(testEquality(std::ranges::next(std::as_const(jwv).begin(), 4), jwv.end(), true));
assert(testEquality(std::ranges::next(std::as_const(jwv).begin(), 2), jwv.end(), false));
}

{ // Const == false
AssertEqualityReturnBool<std::ranges::iterator_t<JWV>, std::ranges::sentinel_t<const JWV>>();
JWV jwv(V{Inner{13}, Inner{15, 16}}, 14);
assert(testEquality(std::ranges::next(jwv.begin(), 4), std::as_const(jwv).end(), true));
assert(testEquality(std::ranges::next(jwv.begin(), 3), std::as_const(jwv).end(), false));
}
}

{ // Check invalid comparisons between iterator<Const> and sentinel<!Const>
using JWV2 = std::ranges::join_with_view<NonCrossConstComparableView, Pattern>;
static_assert(!std::ranges::common_range<JWV2>);

static_assert(!weakly_equality_comparable_with<std::ranges::iterator_t<const JWV2>, std::ranges::sentinel_t<JWV2>>);
static_assert(!weakly_equality_comparable_with<std::ranges::iterator_t<JWV2>, std::ranges::sentinel_t<const JWV2>>);

// Those should be valid
static_assert(weakly_equality_comparable_with<std::ranges::iterator_t<JWV2>, std::ranges::sentinel_t<JWV2>>);
static_assert(
weakly_equality_comparable_with<std::ranges::iterator_t<const JWV2>, std::ranges::sentinel_t<const JWV2>>);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());

return 0;
}
Loading

0 comments on commit a9a1031

Please sign in to comment.