[libcxx-commits] [libcxx] [libc++] Implement `views::join_with` (PR #65536)
Jakub Mazurkiewicz via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Feb 2 16:36:20 PST 2024
https://github.com/JMazurkiewicz updated https://github.com/llvm/llvm-project/pull/65536
>From f1433c59ed2423eb85652ab90dbf4bcbf1bd5904 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:42:52 +0100
Subject: [PATCH 01/16] [libc++] Implement `views::join_with`
* Add **experimental** implementation of P2441R2: `views::join_with` (https://wg21.link/P2441R2):
* `views::join_with` cannot be used in constant expression because P2231R1 hasn't been implemented
* There are some issues with current implementation, see LWG-3971 for example
* Feature test macro `__cpp_lib_ranges_join_with` is not defined
* 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)
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__ranges/join_with_view.h | 457 +++++++++++++++++++++++
libcxx/include/module.modulemap.in | 4 +
libcxx/include/ranges | 13 +
libcxx/modules/std/ranges.inc | 14 +-
5 files changed, 483 insertions(+), 6 deletions(-)
create mode 100644 libcxx/include/__ranges/join_with_view.h
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 6a845c813dc33..17acb327d4f1c 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -632,6 +632,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
diff --git a/libcxx/include/__ranges/join_with_view.h b/libcxx/include/__ranges/join_with_view.h
new file mode 100644
index 0000000000000..fbab49c4efe99
--- /dev/null
+++ b/libcxx/include/__ranges/join_with_view.h
@@ -0,0 +1,457 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___RANGES_JOIN_WITH_VIEW_H
+#define _LIBCPP___RANGES_JOIN_WITH_VIEW_H
+
+#include <__concepts/common_reference_with.h>
+#include <__concepts/common_with.h>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__concepts/derived_from.h>
+#include <__config>
+#include <__functional/bind_back.h>
+#include <__iterator/concepts.h>
+#include <__iterator/incrementable_traits.h>
+#include <__iterator/iter_move.h>
+#include <__iterator/iter_swap.h>
+#include <__iterator/iterator_traits.h>
+#include <__memory/addressof.h>
+#include <__ranges/access.h>
+#include <__ranges/all.h>
+#include <__ranges/concepts.h>
+#include <__ranges/non_propagating_cache.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/single_view.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/decay.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/maybe_const.h>
+#include <__utility/as_const.h>
+#include <__utility/as_lvalue.h>
+#include <__utility/empty.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <variant>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
+
+namespace ranges {
+template <class _Range, class _Pattern>
+concept __compatible_joinable_ranges =
+ common_with<range_value_t<_Range>, range_value_t<_Pattern>> &&
+ common_reference_with<range_reference_t<_Range>, range_reference_t<_Pattern>> &&
+ common_reference_with<range_rvalue_reference_t<_Range>, range_rvalue_reference_t<_Pattern>>;
+
+template <class _Range>
+concept __bidirectional_common = bidirectional_range<_Range> && common_range<_Range>;
+
+template <input_range _View, forward_range _Pattern>
+ requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
+ __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+class join_with_view : public view_interface<join_with_view<_View, _Pattern>> {
+ using _InnerRng = range_reference_t<_View>;
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View();
+
+ static constexpr bool _UseOuterItCache = !forward_range<_View>;
+ using _OuterItCache = _If<_UseOuterItCache, __non_propagating_cache<iterator_t<_View>>, __empty_cache>;
+ _LIBCPP_NO_UNIQUE_ADDRESS _OuterItCache __outer_it_;
+
+ static constexpr bool _UseInnerCache = !is_reference_v<_InnerRng>;
+ using _InnerCache = _If<_UseInnerCache, __non_propagating_cache<remove_cvref_t<_InnerRng>>, __empty_cache>;
+ _LIBCPP_NO_UNIQUE_ADDRESS _InnerCache __inner_;
+
+ _LIBCPP_NO_UNIQUE_ADDRESS _Pattern __pattern_ = _Pattern();
+
+ template <bool _Const>
+ struct __iterator;
+
+ template <bool _Const>
+ struct __sentinel;
+
+public:
+ _LIBCPP_HIDE_FROM_ABI join_with_view()
+ requires default_initializable<_View> && default_initializable<_Pattern>
+ = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit join_with_view(_View __base, _Pattern __pattern)
+ : __base_(std::move(__base)), __pattern_(std::move(__pattern)) {}
+
+ template <input_range _Range>
+ requires constructible_from<_View, views::all_t<_Range>> &&
+ constructible_from<_Pattern, single_view<range_value_t<_InnerRng>>>
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit join_with_view(_Range&& __r, range_value_t<_InnerRng> __e)
+ : __base_(views::all(std::forward<_Range>(__r))), __pattern_(views::single(std::move(__e))) {}
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _View base() const&
+ requires copy_constructible<_View>
+ {
+ return __base_;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr _View base() && { return std::move(__base_); }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto begin() {
+ if constexpr (forward_range<_View>) {
+ constexpr bool __use_const = __simple_view<_View> && is_reference_v<_InnerRng> && __simple_view<_Pattern>;
+ return __iterator<__use_const>{*this, ranges::begin(__base_)};
+ } else {
+ __outer_it_.__emplace(ranges::begin(__base_));
+ return __iterator<false>{*this};
+ }
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const
+ requires forward_range<const _View> && forward_range<const _Pattern> &&
+ is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>>
+ {
+ return __iterator<true>{*this, ranges::begin(__base_)};
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto end() {
+ constexpr bool __use_const = __simple_view<_View> && __simple_view<_Pattern>;
+ if constexpr (forward_range<_View> && is_reference_v<_InnerRng> && forward_range<_InnerRng> &&
+ common_range<_View> && common_range<_InnerRng>)
+ return __iterator<__use_const>{*this, ranges::end(__base_)};
+ else
+ return __sentinel<__use_const>{*this};
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto end() const
+ requires forward_range<const _View> && forward_range<const _Pattern> &&
+ is_reference_v<range_reference_t<const _View>> && input_range<range_reference_t<const _View>>
+ {
+ using _InnerConstRng = range_reference_t<const _View>;
+ if constexpr (forward_range<_InnerConstRng> && common_range<const _View> && common_range<_InnerConstRng>)
+ return __iterator<true>{*this, ranges::end(__base_)};
+ else
+ return __sentinel<true>{*this};
+ }
+};
+
+template <class _Range, class _Pattern>
+join_with_view(_Range&&, _Pattern&&) -> join_with_view<views::all_t<_Range>, views::all_t<_Pattern>>;
+
+template <input_range _Range>
+join_with_view(_Range&&, range_value_t<range_reference_t<_Range>>)
+ -> join_with_view<views::all_t<_Range>, single_view<range_value_t<range_reference_t<_Range>>>>;
+
+template <class _Base, class _PatternBase>
+struct __join_with_view_iterator_category {};
+
+template <class _Base, class _PatternBase>
+ requires is_reference_v<range_reference_t<_Base>> && forward_range<_Base> && forward_range<range_reference_t<_Base>>
+struct __join_with_view_iterator_category<_Base, _PatternBase> {
+private:
+ static consteval auto __get_iterator_category() noexcept {
+ using _OuterC = iterator_traits<iterator_t<_Base>>::iterator_category;
+ using _InnerC = iterator_traits<iterator_t<range_reference_t<_Base>>>::iterator_category;
+ using _PatternC = iterator_traits<iterator_t<_PatternBase>>::iterator_category;
+
+ if constexpr (!is_reference_v<common_reference_t<iter_reference_t<iterator_t<range_reference_t<_Base>>>,
+ iter_reference_t<iterator_t<_PatternBase>>>>)
+ return input_iterator_tag{};
+ else if constexpr (derived_from<_OuterC, bidirectional_iterator_tag> &&
+ derived_from<_InnerC, bidirectional_iterator_tag> &&
+ derived_from<_PatternC, bidirectional_iterator_tag> && common_range<range_reference_t<_Base>> &&
+ common_range<_PatternBase>)
+ return bidirectional_iterator_tag{};
+ else if constexpr (derived_from<_OuterC, forward_iterator_tag> && derived_from<_InnerC, forward_iterator_tag> &&
+ derived_from<_PatternC, forward_iterator_tag>)
+ return forward_iterator_tag{};
+ else
+ return input_iterator_tag{};
+ }
+
+public:
+ using iterator_category = decltype(__get_iterator_category());
+};
+
+template <input_range _View, forward_range _Pattern>
+ requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
+ __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+template <bool _Const>
+struct join_with_view<_View, _Pattern>::__iterator
+ : public __join_with_view_iterator_category<__maybe_const<_Const, _View>, __maybe_const<_Const, _Pattern>> {
+private:
+ friend join_with_view;
+
+ using _Parent = __maybe_const<_Const, join_with_view>;
+ using _Base = __maybe_const<_Const, _View>;
+ using _InnerBase = range_reference_t<_Base>;
+ using _PatternBase = __maybe_const<_Const, _Pattern>;
+
+ using _OuterIter = iterator_t<_Base>;
+ using _InnerIter = iterator_t<_InnerBase>;
+ using _PatternIter = iterator_t<_PatternBase>;
+
+ static_assert(!_Const || forward_range<_Base>, "Const can only be true when Base models forward_range.");
+
+ static constexpr bool __ref_is_glvalue = is_reference_v<_InnerBase>;
+
+ _Parent* __parent_ = nullptr;
+
+ static constexpr bool _OuterIterPresent = forward_range<_Base>;
+ using _OuterIterType = _If<_OuterIterPresent, _OuterIter, std::__empty>;
+ _LIBCPP_NO_UNIQUE_ADDRESS _OuterIterType __outer_it_ = _OuterIterType();
+
+ variant<_PatternIter, _InnerIter> __inner_it_;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(_Parent& __parent, _OuterIter __outer)
+ requires forward_range<_Base>
+ : __parent_(std::addressof(__parent)), __outer_it_(std::move(__outer)) {
+ if (__get_outer() != ranges::end(__parent_->__base_)) {
+ __inner_it_.template emplace<1>(ranges::begin(__update_inner()));
+ __satisfy();
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __iterator(_Parent& __parent)
+ requires(!forward_range<_Base>)
+ : __parent_(std::addressof(__parent)) {
+ if (__get_outer() != ranges::end(__parent_->__base_)) {
+ __inner_it_.template emplace<1>(ranges::begin(__update_inner()));
+ __satisfy();
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr _OuterIter& __get_outer() {
+ if constexpr (forward_range<_Base>)
+ return __outer_it_;
+ else
+ return *__parent_->__outer_it_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr const _OuterIter& __get_outer() const {
+ if constexpr (forward_range<_Base>)
+ return __outer_it_;
+ else
+ return *__parent_->__outer_it_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto& __update_inner() {
+ if constexpr (__ref_is_glvalue)
+ return std::__as_lvalue(*__get_outer());
+ else
+ return __parent_->__inner_.__emplace_from([&]() -> decltype(auto) { return *__get_outer(); });
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr auto& __get_inner() {
+ if constexpr (__ref_is_glvalue)
+ return std::__as_lvalue(*__get_outer());
+ else
+ return *__parent_->__inner_;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void __satisfy() {
+ while (true) {
+ if (__inner_it_.index() == 0) {
+ if (std::get<0>(__inner_it_) != ranges::end(__parent_->__pattern_))
+ break;
+
+ __inner_it_.template emplace<1>(ranges::begin(__update_inner()));
+ } else {
+ if (std::get<1>(__inner_it_) != ranges::end(__get_inner()))
+ break;
+
+ if (++__get_outer() == ranges::end(__parent_->__base_)) {
+ if constexpr (__ref_is_glvalue)
+ __inner_it_.template emplace<0>();
+
+ break;
+ }
+
+ __inner_it_.template emplace<0>(ranges::begin(__parent_->__pattern_));
+ }
+ }
+ }
+
+ static consteval auto __get_iterator_concept() noexcept {
+ if constexpr (__ref_is_glvalue && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> &&
+ __bidirectional_common<_PatternBase>)
+ return bidirectional_iterator_tag{};
+ else if constexpr (__ref_is_glvalue && forward_range<_Base> && forward_range<_InnerBase>)
+ return forward_iterator_tag{};
+ else
+ return input_iterator_tag{};
+ }
+
+public:
+ using iterator_concept = decltype(__get_iterator_concept());
+ using value_type = common_type_t<iter_value_t<_InnerIter>, iter_value_t<_PatternIter>>;
+ using difference_type =
+ common_type_t<iter_difference_t<_OuterIter>, iter_difference_t<_InnerIter>, iter_difference_t<_PatternIter>>;
+
+ _LIBCPP_HIDE_FROM_ABI __iterator() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator(__iterator<!_Const> __i)
+ requires _Const && convertible_to<iterator_t<_View>, _OuterIter> &&
+ convertible_to<iterator_t<_InnerRng>, _InnerIter> && convertible_to<iterator_t<_Pattern>, _PatternIter>
+ : __parent_(__i.__parent_), __outer_it_(std::move(__i.__outer_it_)) {
+ if (__i.__inner_it_.index() == 0) {
+ __inner_it_.template emplace<0>(std::get<0>(std::move(__i.__inner_it_)));
+ } else {
+ __inner_it_.template emplace<1>(std::get<1>(std::move(__i.__inner_it_)));
+ }
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator*() const {
+ using __reference = common_reference_t<iter_reference_t<_InnerIter>, iter_reference_t<_PatternIter>>;
+ return std::visit([](auto& __it) -> __reference { return *__it; }, __inner_it_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator++() {
+ std::visit([](auto& __it) { ++__it; }, __inner_it_);
+ __satisfy();
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr void operator++(int) { ++*this; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator++(int)
+ requires __ref_is_glvalue && forward_iterator<_OuterIter> && forward_iterator<_InnerIter>
+ {
+ __iterator __tmp = *this;
+ ++*this;
+ return __tmp;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator& operator--()
+ requires __ref_is_glvalue
+ && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && __bidirectional_common<_PatternBase>
+ {
+ if (__outer_it_ == ranges::end(__parent_->__base_)) {
+ auto&& __inner = *--__outer_it_;
+ __inner_it_.template emplace<1>(ranges::end(__inner));
+ }
+
+ while (true) {
+ if (__inner_it_.index() == 0) {
+ auto& __it = std::get<0>(__inner_it_);
+ if (__it == ranges::begin(__parent_->__pattern_)) {
+ auto&& __inner = *--__outer_it_;
+ __inner_it_.template emplace<1>(ranges::end(__inner));
+ } else
+ break;
+ } else {
+ auto& __it = std::get<1>(__inner_it_);
+ auto&& __inner = *__outer_it_;
+ if (__it == ranges::begin(__inner))
+ __inner_it_.template emplace<0>(ranges::end(__parent_->__pattern_));
+ else
+ break;
+ }
+ }
+
+ std::visit([](auto& __it) { --__it; }, __inner_it_);
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __iterator operator--(int)
+ requires __ref_is_glvalue
+ && bidirectional_range<_Base> && __bidirectional_common<_InnerBase> && __bidirectional_common<_PatternBase>
+ {
+ __iterator __tmp = *this;
+ --*this;
+ return __tmp;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __iterator& __x, const __iterator& __y)
+ requires __ref_is_glvalue && forward_range<_Base> && equality_comparable<_InnerIter>
+ {
+ return __x.__outer_it_ == __y.__outer_it_ && __x.__inner_it_ == __y.__inner_it_;
+ }
+
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI 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 std::visit<__rvalue_reference>(ranges::iter_move, __x.__inner_it_);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y)
+ requires indirectly_swappable<_InnerIter, _PatternIter>
+ {
+ std::visit(ranges::iter_swap, __x.__inner_it_, __y.__inner_it_);
+ }
+};
+
+template <input_range _View, forward_range _Pattern>
+ requires view<_View> && input_range<range_reference_t<_View>> && view<_Pattern> &&
+ __compatible_joinable_ranges<range_reference_t<_View>, _Pattern>
+template <bool _Const>
+struct join_with_view<_View, _Pattern>::__sentinel {
+private:
+ friend join_with_view;
+
+ using _Parent = __maybe_const<_Const, join_with_view>;
+ using _Base = __maybe_const<_Const, _View>;
+
+ sentinel_t<_Base> __end_ = sentinel_t<_Base>();
+
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit __sentinel(_Parent& __parent) : __end_(ranges::end(__parent.__base_)) {}
+
+public:
+ _LIBCPP_HIDE_FROM_ABI __sentinel() = default;
+
+ _LIBCPP_HIDE_FROM_ABI constexpr __sentinel(__sentinel<!_Const> __s)
+ requires _Const && convertible_to<sentinel_t<_View>, sentinel_t<_Base>>
+ : __end_(std::move(__s.__end_)) {}
+
+ template <bool _OtherConst>
+ requires sentinel_for<sentinel_t<_Base>, iterator_t<__maybe_const<_OtherConst, _View>>>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
+ return __x.__get_outer() == __y.__end_;
+ }
+};
+
+namespace views {
+namespace __join_with_view {
+struct __fn {
+ template <class _Range, class _Pattern>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range, _Pattern&& __pattern) const
+ noexcept(noexcept(/**/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))))
+ -> decltype(/*--*/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))) {
+ return /*-------------*/ join_with_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern));
+ }
+
+ template <class _Pattern>
+ requires constructible_from<decay_t<_Pattern>, _Pattern>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Pattern&& __pattern) const
+ noexcept(is_nothrow_constructible_v<decay_t<_Pattern>, _Pattern>) {
+ return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pattern>(__pattern)));
+ }
+};
+} // namespace __join_with_view
+
+inline namespace __cpo {
+inline constexpr auto join_with = __join_with_view::__fn{};
+} // namespace __cpo
+} // namespace views
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_JOIN_WITH_VIEW_H
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 03f71290b6603..66b292af9d884 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1671,6 +1671,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
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 660d533b2a783..892e11de89725 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -280,6 +280,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
@@ -397,6 +409,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>
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 82c7d99f8979a..399d8983b4d08 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -122,7 +122,7 @@ export namespace std {
namespace views {
using std::ranges::views::repeat;
- } // namespace views
+ } // namespace views
#endif // _LIBCPP_STD_VER >= 23
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
@@ -159,7 +159,7 @@ export namespace std {
namespace views {
using std::ranges::views::as_rvalue;
- } // namespace views
+ } // namespace views
#endif // _LIBCPP_STD_VER >= 23
// [range.filter], filter view
@@ -209,13 +209,15 @@ export namespace std {
namespace views {
using std::ranges::views::join;
} // namespace views
-#if 0
+
+#if _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
using std::ranges::join_with_view;
namespace views {
using std::ranges::views::join_with;
- } // namespace views
-#endif
+ } // namespace views
+#endif // _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
+
using std::ranges::lazy_split_view;
// [range.split], split view
@@ -271,7 +273,7 @@ export namespace std {
namespace views {
using std::ranges::views::zip;
- } // namespace views
+ } // namespace views
#endif // _LIBCPP_STD_VER >= 23
#if 0
>From d8a6690d26e9ad0a2082068b12f8126c0c77914a Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:45:40 +0100
Subject: [PATCH 02/16] Update `transitive_includes` files
---
libcxx/test/libcxx/transitive_includes/cxx03.csv | 1 +
libcxx/test/libcxx/transitive_includes/cxx11.csv | 1 +
libcxx/test/libcxx/transitive_includes/cxx14.csv | 1 +
libcxx/test/libcxx/transitive_includes/cxx17.csv | 1 +
libcxx/test/libcxx/transitive_includes/cxx20.csv | 1 +
5 files changed, 5 insertions(+)
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index d4c22023db7a0..36394c7cca31d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -648,6 +648,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
+ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 5eb839a3aa7a6..3da979ccb3d24 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -653,6 +653,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
+ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 7b372fb37dcd5..1aee85a674159 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -655,6 +655,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
+ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 7b372fb37dcd5..1aee85a674159 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -655,6 +655,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
+ranges variant
ranges version
ratio climits
ratio cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 9b4915a468d1c..ca4637c3525a5 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -660,6 +660,7 @@ ranges optional
ranges span
ranges tuple
ranges type_traits
+ranges variant
ranges version
ratio climits
ratio cstdint
>From 4ae6585871576644974ed19adf86f4b38d5615ed Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:48:12 +0100
Subject: [PATCH 03/16] Add helper types
---
.../range.adaptors/range.join.with/types.h | 268 ++++++++++++++++++
1 file changed, 268 insertions(+)
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
new file mode 100644
index 0000000000000..cdbc404bfd789
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
@@ -0,0 +1,268 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
+#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
+
+#include <initializer_list>
+#include <ranges>
+#include <string>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "test_iterators.h"
+#include "test_macros.h"
+#include "test_range.h"
+
+template <class Tp>
+void pass_value(Tp);
+
+template <class Tp, class... Args>
+concept ConstructionIsExplicit = std::constructible_from<Tp, Args...> && !requires(Args&&... args) {
+ pass_value<Tp>({std::forward<Args>(args)...});
+};
+
+struct ViewProperties {
+ bool simple = false;
+ bool common = true;
+};
+
+template <std::ranges::input_range Data,
+ ViewProperties Prop,
+ template <class...>
+ class It,
+ template <class...> class ConstIt = It>
+class BasicView : public std::ranges::view_base {
+ Data data_;
+
+public:
+ constexpr BasicView()
+ requires std::default_initializable<Data>
+ = default;
+
+ template <class R>
+ constexpr explicit BasicView(R&& r)
+ requires requires { std::ranges::to<Data>(std::forward<R>(r)); }
+ /*******/ : data_(std::ranges::to<Data>(std::forward<R>(r))) {}
+
+ constexpr explicit BasicView(std::initializer_list<std::ranges::range_value_t<Data>> il)
+ : data_(std::ranges::to<Data>(il)) {}
+
+ constexpr auto begin()
+ requires(!Prop.simple)
+ {
+ return It(data_.begin());
+ }
+
+ constexpr auto end()
+ requires(!Prop.simple)
+ {
+ if constexpr (Prop.common)
+ return It(data_.end());
+ else
+ return sentinel_wrapper(It(data_.end()));
+ }
+
+ constexpr auto begin() const { return ConstIt(data_.begin()); }
+
+ constexpr auto end() const {
+ if constexpr (Prop.common)
+ return ConstIt(data_.end());
+ else
+ return sentinel_wrapper(ConstIt(data_.end()));
+ }
+};
+
+template <class Tp, ViewProperties Prop, template <class...> class It, template <class...> class ConstIt = It>
+using BasicVectorView = BasicView<std::vector<Tp>, Prop, It, ConstIt>;
+
+struct AsPrvalue {
+ template <class Tp>
+ constexpr auto operator()(Tp&& t) const {
+ return std::forward<Tp>(t);
+ }
+};
+
+template <class Tp>
+class RvalueVector {
+ using Vec = std::vector<Tp>;
+ std::ranges::transform_view<std::ranges::owning_view<Vec>, AsPrvalue> range_;
+
+public:
+ constexpr RvalueVector() = default;
+ constexpr explicit RvalueVector(Vec vec) : range_(std::move(vec), AsPrvalue{}) {}
+ constexpr explicit RvalueVector(std::initializer_list<Tp> il) : RvalueVector(Vec(il)) {}
+
+ constexpr auto begin() { return range_.begin(); }
+ constexpr auto end() { return range_.end(); }
+ constexpr auto begin() const { return range_.begin(); }
+ constexpr auto end() const { return range_.end(); }
+};
+
+template <class It>
+class DefaultCtorInputIter {
+ It it_ = It();
+
+public:
+ using value_type = std::iter_value_t<It>;
+ using difference_type = std::iter_difference_t<It>;
+
+ DefaultCtorInputIter() = default;
+ constexpr explicit DefaultCtorInputIter(It it) : it_(it) {}
+
+ constexpr DefaultCtorInputIter& operator++() {
+ ++it_;
+ return *this;
+ }
+
+ constexpr void operator++(int) { ++*this; }
+ constexpr decltype(auto) operator*() const { return *it_; }
+ constexpr bool operator==(const DefaultCtorInputIter&) const = default;
+};
+
+template <class It>
+DefaultCtorInputIter(It) -> DefaultCtorInputIter<It>;
+
+template <class Tp>
+class InputRangeButOutputWhenConst {
+ using Vec = std::vector<Tp>;
+ std::ranges::ref_view<Vec> range_;
+
+public:
+ constexpr explicit InputRangeButOutputWhenConst(Vec& vec) : range_(vec) {}
+
+ constexpr auto begin() { return cpp20_input_iterator(range_.begin()); }
+ constexpr auto end() { return sentinel_wrapper(cpp20_input_iterator(range_.end())); }
+ constexpr auto begin() const { return cpp20_output_iterator(range_.begin()); }
+ constexpr auto end() const { return sentinel_wrapper(cpp20_output_iterator(range_.end())); }
+};
+
+template <class Tp>
+using ForwardViewButInputWhenConst =
+ BasicVectorView<Tp, ViewProperties{.common = false}, forward_iterator, cpp20_input_iterator>;
+
+template <class It>
+class ForwardIteratorWithInputCategory {
+ using Self = ForwardIteratorWithInputCategory;
+ It it_ = It();
+
+public:
+ using value_type = std::iter_value_t<It>;
+ using difference_type = std::iter_difference_t<It>;
+ using iterator_concept = std::forward_iterator_tag;
+ using iterator_category = std::input_iterator_tag;
+
+ ForwardIteratorWithInputCategory() = default;
+ explicit ForwardIteratorWithInputCategory(It it);
+
+ std::iter_reference_t<It> operator*() const;
+ Self& operator++();
+ Self operator++(int);
+ friend bool operator==(const Self&, const Self&);
+};
+
+template <class It>
+explicit ForwardIteratorWithInputCategory(It) -> ForwardIteratorWithInputCategory<It>;
+
+template <class It>
+class EqComparableInputIter {
+ It it_;
+
+public:
+ using value_type = std::iter_value_t<It>;
+ using difference_type = std::iter_difference_t<It>;
+
+ constexpr explicit EqComparableInputIter(It it) : it_(it) {}
+
+ constexpr decltype(auto) operator*() const { return *it_; }
+ constexpr EqComparableInputIter& operator++() {
+ ++it_;
+ return *this;
+ }
+ constexpr void operator++(int) { ++it_; }
+
+ friend constexpr It base(const EqComparableInputIter& i) { return i.it_; }
+
+ friend constexpr bool operator==(const EqComparableInputIter& left, const EqComparableInputIter& right) {
+ return left.it_ == right.it_;
+ }
+};
+
+template <class It>
+EqComparableInputIter(It) -> EqComparableInputIter<It>;
+
+template <class Val>
+struct ConstOppositeView : std::ranges::view_base {
+ const Val* begin();
+ sentinel_wrapper<const Val*> end();
+ Val* begin() const;
+ sentinel_wrapper<Val*> end() const;
+};
+
+namespace selftest {
+using BV1 = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
+static_assert(std::ranges::forward_range<BV1>);
+static_assert(!std::ranges::bidirectional_range<BV1>);
+static_assert(std::ranges::common_range<BV1>);
+static_assert(simple_view<BV1>);
+
+using BV2 =
+ BasicView<RvalueVector<std::string>, ViewProperties{.simple = false, .common = false}, cpp20_input_iterator>;
+static_assert(std::ranges::input_range<BV2>);
+static_assert(!std::ranges::forward_range<BV2>);
+static_assert(!std::ranges::common_range<BV2>);
+static_assert(!std::is_reference_v<std::ranges::range_reference_t<BV2>>);
+static_assert(!simple_view<BV2>);
+
+using RV = RvalueVector<int>;
+static_assert(std::movable<RV>);
+static_assert(std::ranges::random_access_range<RV>);
+static_assert(std::ranges::random_access_range<const RV>);
+static_assert(!std::is_reference_v<std::ranges::range_reference_t<RV>>);
+static_assert(!std::is_reference_v<std::ranges::range_reference_t<const RV>>);
+
+using DCII = DefaultCtorInputIter<int*>;
+static_assert(std::default_initializable<DCII>);
+static_assert(std::sentinel_for<DCII, DCII>);
+static_assert(std::input_iterator<DCII>);
+static_assert(!std::forward_iterator<DCII>);
+
+using IRBOWC = InputRangeButOutputWhenConst<int>;
+static_assert(std::ranges::input_range<IRBOWC>);
+static_assert(std::ranges::output_range<const IRBOWC&, int>);
+
+using FVBIWC = ForwardViewButInputWhenConst<int>;
+static_assert(std::default_initializable<FVBIWC>);
+static_assert(std::ranges::view<FVBIWC>);
+static_assert(std::ranges::forward_range<FVBIWC>);
+static_assert(!std::ranges::common_range<FVBIWC>);
+static_assert(std::ranges::input_range<const FVBIWC&>);
+static_assert(!std::ranges::forward_range<const FVBIWC&>);
+static_assert(!std::ranges::common_range<const FVBIWC&>);
+
+using FIWIC = ForwardIteratorWithInputCategory<long*>;
+static_assert(std::forward_iterator<FIWIC>);
+static_assert(std::same_as<FIWIC::iterator_category, std::input_iterator_tag>);
+static_assert(std::same_as<FIWIC::iterator_category, std::iterator_traits<FIWIC>::iterator_category>);
+
+using ECII = EqComparableInputIter<int*>;
+static_assert(std::input_iterator<ECII>);
+static_assert(!std::forward_iterator<ECII>);
+static_assert(std::equality_comparable<ECII>);
+
+using COV = ConstOppositeView<int>;
+static_assert(std::ranges::view<COV>);
+static_assert(std::ranges::range<const COV>);
+static_assert(!std::ranges::common_range<COV>);
+static_assert(!std::ranges::common_range<const COV>);
+static_assert(std::convertible_to<std::ranges::iterator_t<const COV>, std::ranges::iterator_t<COV>>);
+static_assert(!std::convertible_to<std::ranges::iterator_t<COV>, std::ranges::iterator_t<const COV>>);
+} // namespace selftest
+
+#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_WITH_TYPES_H
>From 4e157cb6af4efe9f4b7a2945d59c539a34002d59 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:49:29 +0100
Subject: [PATCH 04/16] Test `[range.join.with.view]`
---
.../range.join.with.view/base.pass.cpp | 149 ++++++++++
.../range.join.with.view/begin.pass.cpp | 260 +++++++++++++++++
.../constraints.compile.pass.cpp | 267 ++++++++++++++++++
.../ctad.compile.pass.cpp | 231 +++++++++++++++
.../ctor.default.pass.cpp | 76 +++++
.../ctor.range.element.pass.cpp | 247 ++++++++++++++++
.../ctor.range.pattern.pass.cpp | 114 ++++++++
.../range.join.with.view/end.pass.cpp | 245 ++++++++++++++++
.../inheritance.compile.pass.cpp | 39 +++
9 files changed, 1628 insertions(+)
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctad.compile.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.element.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.pattern.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/inheritance.compile.pass.cpp
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
new file mode 100644
index 0000000000000..1a72ac200f869
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/base.pass.cpp
@@ -0,0 +1,149 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// constexpr View base() const& requires copy_constructible<View>;
+// constexpr View base() &&;
+
+#include <ranges>
+
+#include <cassert>
+#include <utility>
+#include <vector>
+
+using InnerRange = std::vector<int>;
+
+struct Range : std::ranges::view_base {
+ constexpr explicit Range(InnerRange* b, InnerRange* e) : begin_(b), end_(e) {}
+ constexpr Range(const Range& other) : begin_(other.begin_), end_(other.end_), was_copy_initialized_(true) {}
+ constexpr Range(Range&& other) : begin_(other.begin_), end_(other.end_), was_move_initialized_(true) {}
+ Range& operator=(const Range&) = default;
+ Range& operator=(Range&&) = default;
+ constexpr InnerRange* begin() const { return begin_; }
+ constexpr InnerRange* end() const { return end_; }
+
+ InnerRange* begin_;
+ InnerRange* end_;
+ bool was_copy_initialized_ = false;
+ bool was_move_initialized_ = false;
+};
+
+static_assert(std::ranges::view<Range>);
+static_assert(std::ranges::input_range<Range>);
+
+struct Pattern : std::ranges::view_base {
+ static constexpr int pat[2] = {0, 0};
+ constexpr const int* begin() const { return pat; }
+ constexpr const int* end() const { return pat + 2; }
+};
+
+static_assert(std::ranges::view<Pattern>);
+static_assert(std::ranges::forward_range<Pattern>);
+
+template <class Tp>
+struct NonCopyableRange : std::ranges::view_base {
+ NonCopyableRange(const NonCopyableRange&) = delete;
+ NonCopyableRange(NonCopyableRange&&) = default;
+ NonCopyableRange& operator=(const NonCopyableRange&) = default;
+ NonCopyableRange& operator=(NonCopyableRange&&) = default;
+ Tp* begin() const;
+ Tp* end() const;
+};
+
+static_assert(!std::copy_constructible<NonCopyableRange<InnerRange>>);
+static_assert(!std::copy_constructible<NonCopyableRange<int>>);
+
+template <typename T>
+concept CanCallBaseOn = requires(T&& t) { std::forward<T>(t).base(); };
+
+constexpr bool test() {
+ InnerRange buff[3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
+ Pattern pattern;
+
+ // Check the const& overload
+ {
+ Range range(buff, buff + 3);
+ std::ranges::join_with_view<Range, Pattern> view(range, pattern);
+ std::same_as<Range> decltype(auto) result = view.base();
+ assert(result.was_copy_initialized_);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 3);
+ }
+
+ // Check the const& overload on const `view`
+ {
+ Range range(buff, buff + 3);
+ const std::ranges::join_with_view<Range, Pattern> view(range, pattern);
+ std::same_as<Range> decltype(auto) result = view.base();
+ assert(result.was_copy_initialized_);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 3);
+ }
+
+ // Check the && overload
+ {
+ Range range(buff, buff + 3);
+ std::ranges::join_with_view<Range, Pattern> view(range, pattern);
+ std::same_as<Range> decltype(auto) result = std::move(view).base();
+ assert(result.was_move_initialized_);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 3);
+ }
+
+ // Check the && overload on const `view`
+ {
+ Range range(buff, buff + 3);
+ const std::ranges::join_with_view<Range, Pattern> view(range, pattern);
+ std::same_as<Range> decltype(auto) result = std::move(view).base();
+ assert(result.was_copy_initialized_);
+ assert(result.begin() == buff);
+ assert(result.end() == buff + 3);
+ }
+
+ // Ensure the const& overload is not considered when the base is not copy-constructible
+ {
+ static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
+ static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&>);
+ static_assert(!CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
+ static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>&&>);
+ static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, Pattern>>);
+ }
+
+ // Ensure the const& overload does not depend on Pattern's copy-constructability
+ {
+ static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>& >);
+ static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>& >);
+ static_assert(CanCallBaseOn<const std::ranges::join_with_view<Range, NonCopyableRange<int>>&& >);
+ static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>&& >);
+ static_assert(CanCallBaseOn<std::ranges::join_with_view<Range, NonCopyableRange<int>>>);
+ }
+
+ // Check above two at the same time
+ {
+ static_assert(
+ !CanCallBaseOn<const std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
+ static_assert(!CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&>);
+ static_assert(
+ !CanCallBaseOn<const std::ranges::join_with_view< NonCopyableRange<InnerRange>, NonCopyableRange<int>>&&>);
+ static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>&&>);
+ static_assert(CanCallBaseOn<std::ranges::join_with_view<NonCopyableRange<InnerRange>, NonCopyableRange<int>>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
new file mode 100644
index 0000000000000..1d347e3fca09d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/begin.pass.cpp
@@ -0,0 +1,260 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// constexpr auto begin() {
+// if constexpr (forward_range<V>) {
+// constexpr bool use_const =
+// simple-view<V> && is_reference_v<InnerRng> && simple-view<Pattern>;
+// return iterator<use_const>{*this, ranges::begin(base_)};
+// }
+// else {
+// outer_it_ = ranges::begin(base_);
+// return iterator<false>{*this};
+// }
+// }
+// constexpr auto begin() const
+// requires forward_range<const V> &&
+// forward_range<const Pattern> &&
+// is_reference_v<range_reference_t<const V>> &&
+// input_range<range_reference_t<const V>> {
+// return iterator<true>{*this, ranges::begin(base_)};
+// }
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <string>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <bool Simple>
+using MaybeSimpleForwardView = BasicView<std::vector<std::string>, ViewProperties{.simple = Simple}, forward_iterator>;
+
+template <bool Simple>
+using MaybeSimpleForwardRvalueView =
+ BasicView<RvalueVector<std::string>, ViewProperties{.simple = Simple}, forward_iterator>;
+
+template <bool Simple>
+using MaybeSimplePattern = BasicView<std::string, ViewProperties{.simple = Simple}, forward_iterator>;
+
+class DifferentWhenConstV {
+ using Arr = std::array<std::array<int, 2>, 2>;
+ static constexpr Arr regular_v = {{{1, 2}, {3, 4}}};
+ static constexpr Arr const_v = {{{5, 6}, {7, 8}}};
+
+public:
+ auto begin() { return regular_v.begin(); }
+ auto end() { return regular_v.end(); }
+ auto begin() const { return const_v.begin(); }
+ auto end() const { return const_v.end(); }
+};
+
+class DifferentWhenConstPattern {
+ using Pat = std::array<int, 2>;
+ static constexpr Pat regular_pattern = {0, 0};
+ static constexpr Pat const_pattern = {1, 1};
+
+public:
+ auto begin() { return regular_pattern.begin(); }
+ auto end() { return regular_pattern.end(); }
+ auto begin() const { return const_pattern.begin(); }
+ auto end() const { return const_pattern.end(); }
+};
+
+template <class V, class Pattern>
+concept JoinWithViewHasConstBegin = requires(const std::ranges::join_with_view<V, Pattern> jwv) {
+ { jwv.begin() } -> std::input_iterator;
+};
+
+constexpr void test_begin() {
+ using Str = std::string;
+ using Vec = std::vector<Str>;
+
+ { // `V` models simple-view
+ // `is_reference_v<InnerRng>` is true
+ // `Pattern` models simple-view
+ // `V` and `Pattern` contain some elements
+ using V = MaybeSimpleForwardView<true>;
+ using Pattern = MaybeSimplePattern<true>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"A", "B", "C"}), Pattern(Str{">>"}));
+ auto it = jwv.begin();
+ assert(std::ranges::equal(std::views::counted(it, 7), Str{"A>>B>>C"}));
+ }
+
+ { // `V` does not model simple-view
+ // `is_reference_v<InnerRng>` is true
+ // `Pattern` models simple-view
+ // `V` and `Pattern` are empty
+ using V = MaybeSimpleForwardView<false>;
+ using Pattern = MaybeSimplePattern<true>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{}), Pattern(Str{}));
+ auto it = jwv.begin();
+ assert(it == jwv.end());
+ }
+
+ { // `V` models simple-view
+ // `is_reference_v<InnerRng>` is false
+ // `Pattern` models simple-view
+ // `V` contains two elements, `Pattern` is empty
+ using V = MaybeSimpleForwardRvalueView<true>;
+ using Pattern = MaybeSimplePattern<true>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"1", "2"}), Pattern(Str{""}));
+ auto it = jwv.begin();
+ assert(*it == '1');
+ assert(*++it == '2');
+ }
+
+ { // `V` models simple-view
+ // `is_reference_v<InnerRng>` is true
+ // `Pattern` does not model simple-view
+ // `V` contains one element, `Pattern` is empty
+ using V = MaybeSimpleForwardView<true>;
+ using Pattern = MaybeSimplePattern<false>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"07"}), Pattern(Str{}));
+ auto it = jwv.begin();
+ assert(*it++ == '0');
+ assert(*it == '7');
+ }
+
+ { // `V` does not model simple-view
+ // `is_reference_v<InnerRng>` is false
+ // `Pattern` models simple-view
+ // `V` contains three elements (2nd is empty), `Pattern` is not empty
+ using V = MaybeSimpleForwardRvalueView<false>;
+ using Pattern = MaybeSimplePattern<true>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"A", "", "C"}), Pattern(Str{"--"}));
+ auto it = jwv.begin();
+ assert(std::ranges::equal(std::views::counted(it, 6), Str("A----C")));
+ }
+
+ { // `V` does not model simple-view
+ // `is_reference_v<InnerRng>` is true
+ // `Pattern` does not model simple-view
+ // `V` contains some empty elements, `Pattern` is not empty
+ using V = MaybeSimpleForwardView<false>;
+ using Pattern = MaybeSimplePattern<false>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", "", ""}), Pattern(Str{"-"}));
+ auto it = jwv.begin();
+ assert(*it++ == '-');
+ assert(*it == '-');
+ }
+
+ { // `V` models simple-view
+ // `is_reference_v<InnerRng>` is false
+ // `Pattern` does not model simple-view
+ // `V` contains two elements, `Pattern` is not empty
+ using V = MaybeSimpleForwardRvalueView<true>;
+ using Pattern = MaybeSimplePattern<false>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"X", "Z"}), Pattern(Str{"Y"}));
+ auto it = jwv.begin();
+ assert(*it == 'X');
+ assert(*++it == 'Y');
+ assert(*++it == 'Z');
+ }
+
+ { // `V` does not model simple-view
+ // `is_reference_v<InnerRng>` is false
+ // `Pattern` does not model simple-view
+ // `V` contains two empty elements, `Pattern` is not empty
+ using V = MaybeSimpleForwardRvalueView<false>;
+ using Pattern = MaybeSimplePattern<false>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", ""}), Pattern(Str{"?"}));
+ auto it = jwv.begin();
+ assert(*it == '?');
+ assert(++it == jwv.end());
+ }
+
+ { // `V` does not model forward range
+ // `V` contains some empty elements, `Pattern` is empty
+ using V = BasicView<Vec, ViewProperties{.common = false}, cpp20_input_iterator>;
+ using Pattern = MaybeSimplePattern<false>;
+ std::ranges::join_with_view<V, Pattern> jwv(V(Vec{"", "", ""}), Pattern(Str{""}));
+ auto it = jwv.begin();
+ assert(it == jwv.end());
+ }
+}
+
+constexpr void test_const_begin() {
+ { // `const V` models forward range
+ // `const Pattern` models forward range
+ // `is_reference_v<range_reference_t<const V>>` is true
+ // `range_reference_t<const V>` models input range
+ using V = BasicView<DifferentWhenConstV, ViewProperties{}, forward_iterator>;
+ using Pattern = BasicView<DifferentWhenConstPattern, ViewProperties{}, forward_iterator>;
+
+ const std::ranges::join_with_view<V, Pattern> jwv;
+ auto it = jwv.begin();
+ assert(std::ranges::equal(std::views::counted(it, 6), std::array{5, 6, 1, 1, 7, 8}));
+ }
+
+ using Vec = std::vector<std::array<int, 2>>;
+ using Pat = std::array<int, 2>;
+
+ { // `const V` does not model forward range
+ // `const Pattern` models forward range
+ // `is_reference_v<range_reference_t<const V>>` is true
+ // `range_reference_t<const V>` models input range
+ static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{.common = false}, cpp20_input_iterator>,
+ BasicView<Pat, ViewProperties{}, forward_iterator>>);
+ }
+
+ { // `const V` models forward range
+ // `const Pattern` does not model forward range
+ // `is_reference_v<range_reference_t<const V>>` is true
+ // `range_reference_t<const V>` models input range
+ static_assert(!JoinWithViewHasConstBegin<BasicView<Vec, ViewProperties{}, forward_iterator>,
+ BasicView<Pat, ViewProperties{.common = false}, cpp20_input_iterator>>);
+ }
+
+ { // `const V` models forward range
+ // `const Pattern` models forward range
+ // `is_reference_v<range_reference_t<const V>>` is false
+ // `range_reference_t<const V>` models input range
+ static_assert(
+ !JoinWithViewHasConstBegin<BasicView<RvalueVector<std::vector<int>>, ViewProperties{}, forward_iterator>,
+ BasicView<Pat, ViewProperties{}, forward_iterator>>);
+ }
+
+ { // `const V` models forward range
+ // `const Pattern` models forward range
+ // `is_reference_v<range_reference_t<const V>>` is true
+ // `range_reference_t<const V>` does not model input range
+ static_assert(!JoinWithViewHasConstBegin<
+ BasicView<std::vector<InputRangeButOutputWhenConst<int>>, ViewProperties{}, forward_iterator>,
+ BasicView<Pat, ViewProperties{}, forward_iterator>>);
+ }
+
+ { // Check situation when iterators returned by `begin()` and `begin() const` are the same
+ using JWV = std::ranges::join_with_view<MaybeSimpleForwardView<true>, MaybeSimplePattern<true>>;
+ static_assert(std::same_as<std::ranges::iterator_t<JWV&>, std::ranges::iterator_t<const JWV&>>);
+ }
+}
+
+constexpr bool test() {
+ test_begin();
+ test_const_begin();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
new file mode 100644
index 0000000000000..683d36417d1a3
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/constraints.compile.pass.cpp
@@ -0,0 +1,267 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// 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;
+
+#include <ranges>
+
+#include <cstddef>
+#include <vector>
+
+#include "test_iterators.h"
+
+template <class View, class Pattern>
+concept CanFormJoinWithView = requires { typename std::ranges::join_with_view<View, Pattern>; };
+
+// join_with_view is not valid when `V` is not an input_range
+namespace test_when_view_is_not_an_input_range {
+struct View : std::ranges::view_base {
+ using It = cpp20_output_iterator<std::vector<int>*>;
+ It begin();
+ sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+ int* begin();
+ int* end();
+};
+
+static_assert(std::ranges::range<View>);
+static_assert(!std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_view_is_not_an_input_range
+
+// join_with_view is not valid when `Pattern` is not a forward_range
+namespace test_when_pattern_is_not_a_forward_range {
+struct View : std::ranges::view_base {
+ std::vector<float>* begin();
+ std::vector<float>* end();
+};
+
+struct Pattern : std::ranges::view_base {
+ using It = cpp20_input_iterator<float*>;
+ It begin();
+ sentinel_wrapper<It> end();
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+static_assert(!std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_pattern_is_not_a_forward_range
+
+// join_with_view is not valid when `V` does not model std::ranges::view
+namespace test_when_view_does_not_model_view {
+struct View {
+ std::vector<double>* begin();
+ std::vector<double>* end();
+};
+
+struct Pattern : std::ranges::view_base {
+ double* begin();
+ double* end();
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(!std::ranges::view<View>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_view_does_not_model_view
+
+// join_with_view is not valid when `range_reference_t` of `V` is not an input_range
+namespace test_when_range_reference_t_of_view_is_not_an_input_range {
+struct InnerRange {
+ using It = cpp20_output_iterator<long*>;
+ It begin();
+ sentinel_wrapper<It> end();
+};
+
+struct View : std::ranges::view_base {
+ InnerRange* begin();
+ InnerRange* end();
+};
+
+struct Pattern : std::ranges::view_base {
+ long* begin();
+ long* end();
+};
+
+static_assert(std::ranges::range<InnerRange>);
+static_assert(!std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_range_reference_t_of_view_is_not_an_input_range
+
+// join_with_view is not valid when `Pattern` does not model std::ranges::view
+namespace test_when_pattern_does_not_model_view {
+struct View : std::ranges::view_base {
+ std::vector<short>* begin();
+ std::vector<short>* end();
+};
+
+struct Pattern {
+ short* begin();
+ short* end();
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(!std::ranges::view<Pattern>);
+static_assert(!CanFormJoinWithView<View, Pattern>);
+} // namespace test_when_pattern_does_not_model_view
+
+// join_with_view is not valid when `range_reference_t<View>` and pattern
+// does not model together compatible-joinable-ranges
+namespace test_when_used_ranges_are_not_compatible_joinable_ranges {
+using std::ranges::range_reference_t;
+using std::ranges::range_rvalue_reference_t;
+using std::ranges::range_value_t;
+
+template <class InnerRange>
+struct View : std::ranges::view_base {
+ InnerRange* begin();
+ InnerRange* end();
+};
+
+namespace no_common_range_value_type {
+struct InnerRange {
+ struct It {
+ using difference_type = ptrdiff_t;
+ struct value_type {};
+
+ struct reference {
+ operator value_type();
+ operator float();
+ };
+
+ It& operator++();
+ void operator++(int);
+ reference operator*() const;
+ };
+
+ It begin();
+ sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+ const float* begin();
+ const float* end();
+};
+
+static_assert(std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(!std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
+static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
+static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
+} // namespace no_common_range_value_type
+
+namespace no_common_range_reference_type {
+struct ValueType {};
+
+struct InnerRange {
+ struct It {
+ using difference_type = ptrdiff_t;
+ using value_type = ValueType;
+ struct reference {
+ operator value_type();
+ };
+
+ It& operator++();
+ void operator++(int);
+ reference operator*() const;
+ };
+
+ It begin();
+ sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+ struct It {
+ using difference_type = ptrdiff_t;
+ using value_type = ValueType;
+ struct reference {
+ operator value_type();
+ };
+
+ It& operator++();
+ It operator++(int);
+ reference operator*() const;
+ bool operator==(const It&) const;
+ friend value_type&& iter_move(const It&);
+ };
+
+ It begin();
+ It end();
+};
+
+static_assert(std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
+static_assert(!std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
+static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
+} // namespace no_common_range_reference_type
+
+namespace no_common_range_rvalue_reference_type {
+struct InnerRange {
+ using It = cpp20_input_iterator<int*>;
+ It begin();
+ sentinel_wrapper<It> end();
+};
+
+struct Pattern : std::ranges::view_base {
+ struct It {
+ using difference_type = ptrdiff_t;
+ struct value_type {
+ operator int() const;
+ };
+
+ struct rvalue_reference {
+ operator value_type();
+ };
+
+ It& operator++();
+ It operator++(int);
+ value_type& operator*() const;
+ bool operator==(const It&) const;
+ friend rvalue_reference iter_move(const It&);
+ };
+
+ It begin();
+ It end();
+};
+
+static_assert(std::ranges::input_range<InnerRange>);
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+static_assert(std::common_with<range_value_t<InnerRange>, range_value_t<Pattern>>);
+static_assert(std::common_reference_with<range_reference_t<InnerRange>, range_reference_t<Pattern>>);
+static_assert(!std::common_reference_with<range_rvalue_reference_t<InnerRange>, range_rvalue_reference_t<Pattern>>);
+static_assert(!CanFormJoinWithView<View<InnerRange>, Pattern>);
+} // namespace no_common_range_rvalue_reference_type
+} // namespace test_when_used_ranges_are_not_compatible_joinable_ranges
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctad.compile.pass.cpp
new file mode 100644
index 0000000000000..c9b4de49bbf1d
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctad.compile.pass.cpp
@@ -0,0 +1,231 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// template<class R, class P>
+// join_with_view(R&&, P&&) -> join_with_view<views::all_t<R>, views::all_t<P>>;
+//
+// template<input_range R>
+// join_with_view(R&&, range_value_t<range_reference_t<R>>)
+// -> join_with_view<views::all_t<R>, single_view<range_value_t<range_reference_t<R>>>>;
+
+#include <ranges>
+
+#include <deque>
+#include <type_traits>
+
+#include "test_iterators.h"
+
+struct View : std::ranges::view_base {
+ using It = cpp20_input_iterator<std::deque<int>*>;
+
+ View() = default;
+ It begin() const;
+ sentinel_wrapper<It> end() const;
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::view<View>);
+
+struct Pattern : std::ranges::view_base {
+ Pattern() = default;
+ forward_iterator<int*> begin();
+ forward_iterator<int*> end();
+};
+
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::view<Pattern>);
+
+// A range that is not a view
+struct Range {
+ using It = cpp20_input_iterator<std::deque<int>*>;
+
+ Range() = default;
+ It begin() const;
+ sentinel_wrapper<It> end() const;
+};
+
+static_assert(std::ranges::input_range<Range>);
+static_assert(!std::ranges::view<Range>);
+
+// A pattern that is not a view
+struct RangePattern {
+ RangePattern() = default;
+ forward_iterator<int*> begin();
+ forward_iterator<int*> end();
+};
+
+static_assert(std::ranges::forward_range<RangePattern>);
+static_assert(!std::ranges::view<RangePattern>);
+
+void test_range_and_pattern_deduction_guide() {
+ { // Both `v` and `pat` model `std::ranges::view`.
+ {
+ View v;
+ Pattern pat;
+ std::ranges::join_with_view view(v, pat);
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
+ }
+ {
+ View v;
+ std::ranges::join_with_view view(v, Pattern{});
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
+ }
+ {
+ Pattern pat;
+ std::ranges::join_with_view view(View{}, pat);
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
+ }
+ {
+ std::ranges::join_with_view view(View{}, Pattern{});
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, Pattern>>);
+ }
+ }
+
+ { // Only `pat` models `std::ranges::view`.
+ {
+ Range v;
+ Pattern pat;
+ std::ranges::join_with_view view(v, pat);
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::ref_view<Range>, Pattern>>);
+ }
+ {
+ Range v;
+ std::ranges::join_with_view view(v, Pattern{});
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::ref_view<Range>, Pattern>>);
+ }
+ {
+ Pattern pat;
+ std::ranges::join_with_view view(Range{}, pat);
+ static_assert(
+ std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::owning_view<Range>, Pattern>>);
+ }
+ {
+ std::ranges::join_with_view view(Range{}, Pattern{});
+ static_assert(
+ std::is_same_v<decltype(view), std::ranges::join_with_view<std::ranges::owning_view<Range>, Pattern>>);
+ }
+ }
+
+ { // Only `v` models `std::ranges::view`.
+ {
+ View v;
+ RangePattern pat;
+ std::ranges::join_with_view view(v, pat);
+ static_assert(
+ std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::ref_view<RangePattern>>>);
+ }
+ {
+ View v;
+ std::ranges::join_with_view view(v, RangePattern{});
+ static_assert(
+ std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::owning_view<RangePattern>>>);
+ }
+ {
+ RangePattern pat;
+ std::ranges::join_with_view view(View{}, pat);
+ static_assert(
+ std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::ref_view<RangePattern>>>);
+ }
+ {
+ std::ranges::join_with_view view(View{}, RangePattern{});
+ static_assert(
+ std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::owning_view<RangePattern>>>);
+ }
+ }
+
+ { // Both `v` and `pat` don't model `std::ranges::view`.
+ {
+ Range r;
+ RangePattern pat;
+ std::ranges::join_with_view view(r, pat);
+ static_assert(std::is_same_v<
+ decltype(view),
+ std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::ref_view<RangePattern>>>);
+ }
+ {
+ Range r;
+ std::ranges::join_with_view view(r, RangePattern{});
+ static_assert(std::is_same_v<
+ decltype(view),
+ std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::owning_view<RangePattern>>>);
+ }
+ {
+ RangePattern pat;
+ std::ranges::join_with_view view(Range{}, pat);
+ static_assert(std::is_same_v<
+ decltype(view),
+ std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::ref_view<RangePattern>>>);
+ }
+ {
+ std::ranges::join_with_view view(Range{}, RangePattern{});
+ static_assert(
+ std::is_same_v<
+ decltype(view),
+ std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::owning_view<RangePattern>>>);
+ }
+ }
+}
+
+void test_range_and_element_deduction_guide() {
+ { // Element is lvalue
+ int elem = 0;
+
+ {
+ View v;
+ std::ranges::join_with_view view(v, elem);
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
+ }
+ {
+ std::ranges::join_with_view view(View{}, elem);
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
+ }
+ {
+ Range r;
+ std::ranges::join_with_view view(r, elem);
+ static_assert(
+ std::is_same_v<decltype(view),
+ std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::single_view<int>>>);
+ }
+ {
+ std::ranges::join_with_view view(Range{}, elem);
+ static_assert(
+ std::is_same_v<decltype(view),
+ std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::single_view<int>>>);
+ }
+ }
+
+ { // Element is rvalue
+ {
+ View v;
+ std::ranges::join_with_view view(v, 1);
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
+ }
+ {
+ std::ranges::join_with_view view(View{}, 1);
+ static_assert(std::is_same_v<decltype(view), std::ranges::join_with_view<View, std::ranges::single_view<int>>>);
+ }
+ {
+ Range r;
+ std::ranges::join_with_view view(r, 1);
+ static_assert(
+ std::is_same_v<decltype(view),
+ std::ranges::join_with_view<std::ranges::ref_view<Range>, std::ranges::single_view<int>>>);
+ }
+ {
+ std::ranges::join_with_view view(Range{}, 1);
+ static_assert(
+ std::is_same_v<decltype(view),
+ std::ranges::join_with_view<std::ranges::owning_view<Range>, std::ranges::single_view<int>>>);
+ }
+ }
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..f6aca6530f957
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.default.pass.cpp
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// join_with_view()
+// requires default_initializable<V> && default_initializable<Pattern> = default;
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+static constexpr auto view = std::to_array<std::array<int, 2>>({{1, 2}, {3, 4}, {5, 6}});
+
+struct TrivialView : std::ranges::view_base {
+ int val_; // intentionally uninitialized
+
+ constexpr auto begin() { return view.data(); }
+ constexpr auto end() { return view.data() + view.size(); }
+};
+
+static_assert(std::is_trivial_v<TrivialView>);
+
+struct NonDefaultConstructibleView : TrivialView {
+ NonDefaultConstructibleView(int);
+};
+
+struct TrivialPattern : std::ranges::view_base {
+ int val_; // intentionally uninitialized
+
+ constexpr int* begin() { return &val_; }
+ constexpr int* end() { return &val_ + 1; }
+};
+
+static_assert(std::is_trivial_v<TrivialPattern>);
+
+struct NonDefaultConstructiblePattern : TrivialPattern {
+ NonDefaultConstructiblePattern(int);
+};
+
+constexpr bool test() {
+ // Check if `base_` and `pattern_` are value initialised
+ {
+ std::ranges::join_with_view<TrivialView, TrivialPattern> v;
+ assert(std::move(v).base().val_ == 0);
+ assert(std::ranges::equal(v, std::array{1, 2, 0, 3, 4, 0, 5, 6}));
+ }
+
+ static_assert(std::default_initializable<std::ranges::join_with_view<TrivialView, TrivialPattern>>);
+ static_assert(!std::default_initializable<std::ranges::join_with_view<TrivialView, NonDefaultConstructiblePattern>>);
+ static_assert(!std::default_initializable<std::ranges::join_with_view<NonDefaultConstructibleView, TrivialPattern>>);
+ static_assert(!std::default_initializable<
+ std::ranges::join_with_view<NonDefaultConstructibleView, NonDefaultConstructiblePattern>>);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.element.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.element.pass.cpp
new file mode 100644
index 0000000000000..c62cf0f58eceb
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.element.pass.cpp
@@ -0,0 +1,247 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// template<input_range R>
+// requires constructible_from<V, views::all_t<R>> &&
+// constructible_from<Pattern, single_view<range_value_t<InnerRng>>>
+// constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> e);
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <type_traits>
+#include <utility>
+
+#include "../types.h"
+#include "test_iterators.h"
+#include "test_range.h"
+
+struct MoveOnlyInt {
+ MoveOnlyInt() = default;
+ MoveOnlyInt(MoveOnlyInt&&) = default;
+ MoveOnlyInt& operator=(MoveOnlyInt&&) = default;
+
+ constexpr MoveOnlyInt(int val) : val_(val) {}
+ constexpr operator int() const { return val_; }
+
+ int val_ = 0;
+};
+
+template <>
+struct std::common_type<MoveOnlyInt, int> {
+ using type = int;
+};
+
+template <>
+struct std::common_type<int, MoveOnlyInt> {
+ using type = int;
+};
+
+struct OutputView : std::ranges::view_base {
+ using It = cpp20_output_iterator<int*>;
+ It begin() const;
+ sentinel_wrapper<It> end() const;
+};
+
+static_assert(std::ranges::output_range<OutputView, int>);
+static_assert(std::ranges::view<OutputView>);
+
+struct InputRange {
+ using It = cpp20_input_iterator<int*>;
+ It begin() const;
+ sentinel_wrapper<It> end() const;
+};
+
+struct InputView : InputRange, std::ranges::view_base {};
+
+static_assert(std::ranges::input_range<InputRange>);
+static_assert(std::ranges::input_range<const InputRange>);
+static_assert(std::ranges::view<InputView>);
+static_assert(std::ranges::input_range<InputView>);
+static_assert(std::ranges::input_range<const InputView>);
+
+class View : public std::ranges::view_base {
+ using OuterRange = std::array<std::array<MoveOnlyInt, 2>, 3>;
+
+ static constexpr OuterRange range_on_input_view = {{{1, 1}, {1, 1}, {1, 1}}};
+ static constexpr OuterRange range_on_ref_input_range = {{{2, 2}, {2, 2}, {2, 2}}};
+ static constexpr OuterRange range_on_const_ref_input_range = {{{3, 3}, {3, 3}, {3, 3}}};
+ static constexpr OuterRange range_on_owning_input_range = {{{4, 4}, {4, 4}, {4, 4}}};
+
+ const OuterRange* r_;
+
+public:
+ // Those functions should never be called in this test.
+ View(View&&) { assert(false); }
+ View(OutputView) { assert(false); }
+ View& operator=(View&&) {
+ assert(false);
+ return *this;
+ }
+
+ constexpr explicit View(InputView) : r_(&range_on_input_view) {}
+ constexpr explicit View(InputRange) = delete;
+ constexpr explicit View(std::ranges::ref_view<InputRange>) : r_(&range_on_ref_input_range) {}
+ constexpr explicit View(std::ranges::ref_view<const InputRange>) : r_(&range_on_const_ref_input_range) {}
+ constexpr explicit View(std::ranges::owning_view<InputRange>) : r_(&range_on_owning_input_range) {}
+
+ constexpr auto begin() const { return r_->begin(); }
+ constexpr auto end() const { return r_->end(); }
+};
+
+static_assert(std::ranges::input_range<View>);
+static_assert(std::ranges::input_range<const View>);
+
+class Pattern : public std::ranges::view_base {
+ int val_;
+
+public:
+ // Those functions should never be called in this test.
+ Pattern(Pattern&&) { assert(false); }
+ template <class T>
+ Pattern(const std::ranges::single_view<T>&) {
+ assert(false);
+ }
+ Pattern& operator=(Pattern&&) {
+ assert(false);
+ return *this;
+ }
+
+ template <class T>
+ constexpr explicit Pattern(std::ranges::single_view<T>&& v) : val_(v[0]) {}
+
+ constexpr const int* begin() const { return &val_; }
+ constexpr const int* end() const { return &val_ + 1; }
+};
+
+static_assert(std::ranges::forward_range<Pattern>);
+static_assert(std::ranges::forward_range<const Pattern>);
+
+constexpr void test_ctor_with_view_and_element() {
+ // Check construction from `r` and `e`, when `r` models `std::ranges::view`
+
+ { // `r` and `e` are glvalues
+ InputView r;
+ int e = 0;
+ std::ranges::join_with_view<View, Pattern> jwv(r, e);
+ assert(std::ranges::equal(jwv, std::array{1, 1, 0, 1, 1, 0, 1, 1}));
+ }
+
+ { // `r` and `e` are const glvalues
+ const InputView r;
+ const int e = 1;
+ std::ranges::join_with_view<View, Pattern> jwv(r, e);
+ assert(std::ranges::equal(jwv, std::array{1, 1, 1, 1, 1, 1, 1, 1}));
+ }
+
+ { // `r` and `e` are prvalues
+ std::ranges::join_with_view<View, Pattern> jwv(InputView{}, MoveOnlyInt{2});
+ assert(std::ranges::equal(jwv, std::array{1, 1, 2, 1, 1, 2, 1, 1}));
+ }
+
+ { // `r` and `e` are xvalues
+ InputView r;
+ MoveOnlyInt e = 3;
+ std::ranges::join_with_view<View, Pattern> jwv(std::move(r), std::move(e));
+ assert(std::ranges::equal(jwv, std::array{1, 1, 3, 1, 1, 3, 1, 1}));
+ }
+
+ // Check explicitness
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView, MoveOnlyInt>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView, int>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputView&, int&>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputView, const int>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputView&, const int&>);
+}
+
+constexpr void test_ctor_with_non_view_and_element() {
+ // Check construction from `r` and `e`, when `r` does not model `std::ranges::view`
+
+ { // `r` and `e` are glvalues
+ InputRange r;
+ int e = 0;
+ std::ranges::join_with_view<View, Pattern> jwv(r, e);
+ assert(std::ranges::equal(jwv, std::array{2, 2, 0, 2, 2, 0, 2, 2}));
+ }
+
+ { // `r` and `e` are const glvalues
+ const InputRange r;
+ const int e = 1;
+ std::ranges::join_with_view<View, Pattern> jwv(r, e);
+ assert(std::ranges::equal(jwv, std::array{3, 3, 1, 3, 3, 1, 3, 3}));
+ }
+
+ { // `r` and `e` are prvalues
+ std::ranges::join_with_view<View, Pattern> jwv(InputRange{}, MoveOnlyInt{2});
+ assert(std::ranges::equal(jwv, std::array{4, 4, 2, 4, 4, 2, 4, 4}));
+ }
+
+ { // `r` and `e` are xvalues
+ InputRange r;
+ MoveOnlyInt e = 3;
+ std::ranges::join_with_view<View, Pattern> jwv(std::move(r), std::move(e));
+ assert(std::ranges::equal(jwv, std::array{4, 4, 3, 4, 4, 3, 4, 4}));
+ }
+
+ // Check explicitness
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange, MoveOnlyInt>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange, int>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, InputRange&, int&>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const InputRange&, const int&>);
+}
+
+constexpr void test_constraints() {
+ { // `R` is not an input range
+ using R = OutputView;
+ static_assert(!std::ranges::input_range<R>);
+ static_assert(std::constructible_from<View, std::views::all_t<R>>);
+ static_assert(std::constructible_from<Pattern, std::ranges::single_view<int>>);
+ static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pattern>, R, int>);
+ }
+
+ { // `V` is not constructible from `views::all_t<R>`
+ using R = test_range<cpp20_input_iterator>;
+ static_assert(std::ranges::input_range<R>);
+ static_assert(!std::constructible_from<View, std::views::all_t<R>>);
+ static_assert(std::constructible_from<Pattern, std::ranges::single_view<int>>);
+ static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pattern>, R, int>);
+ }
+
+ { // `Pattern` is not constructible from `single_view<range_value_t<InnerRng>>`
+ using R = InputView;
+ using Pattern = test_view<forward_iterator>;
+ static_assert(std::ranges::input_range<R>);
+ static_assert(std::constructible_from<View, std::views::all_t<R>>);
+ static_assert(!std::constructible_from<Pattern, std::ranges::single_view<int>>);
+ static_assert(!std::constructible_from<std::ranges::join_with_view<View, Pattern>, R, int>);
+ }
+}
+
+constexpr bool test() {
+ test_ctor_with_view_and_element();
+ test_ctor_with_non_view_and_element();
+ test_constraints();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.pattern.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.pattern.pass.cpp
new file mode 100644
index 0000000000000..26e9b338f77a0
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/ctor.range.pattern.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// constexpr explicit join_with_view(V base, Pattern pattern);
+
+#include <ranges>
+
+#include <algorithm>
+#include <array>
+#include <cassert>
+#include <utility>
+
+#include "../types.h"
+
+class View : public std::ranges::view_base {
+ using OuterRange = std::array<std::array<int, 2>, 3>;
+
+ static constexpr OuterRange default_range = {{{1, 2}, {3, 4}, {5, 6}}};
+ static constexpr OuterRange range_on_move = {{{6, 5}, {4, 3}, {2, 1}}};
+
+ const OuterRange* r_ = &default_range;
+
+public:
+ View() = default;
+ constexpr View(const View&) : r_(&default_range) {}
+ constexpr View(View&&) : r_(&range_on_move) {}
+
+ constexpr View& operator=(View) {
+ r_ = &default_range;
+ return *this;
+ }
+
+ auto begin() { return r_->begin(); }
+ auto end() { return r_->end(); }
+};
+
+class Pattern : public std::ranges::view_base {
+ using PatternRange = std::array<int, 2>;
+
+ static constexpr PatternRange default_range = {0, 0};
+ static constexpr PatternRange range_on_move = {7, 7};
+
+ const PatternRange* val_ = &default_range;
+
+public:
+ Pattern() = default;
+ constexpr Pattern(const Pattern&) : val_(&default_range) {}
+ constexpr Pattern(Pattern&&) : val_(&range_on_move) {}
+
+ constexpr Pattern& operator=(Pattern) {
+ val_ = &default_range;
+ return *this;
+ }
+
+ auto begin() { return val_->begin(); }
+ auto end() { return val_->end(); }
+};
+
+constexpr bool test() {
+ { // Check construction from `view` and `pattern`
+ { // `view` and `pattern` are glvalues
+ View v;
+ Pattern p;
+ std::ranges::join_with_view<View, Pattern> jwv(v, p);
+ assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
+ }
+
+ { // `view` and `pattern` are const glvalues
+ const View v;
+ const Pattern p;
+ std::ranges::join_with_view<View, Pattern> jwv(v, p);
+ assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
+ }
+
+ { // `view` and `pattern` are prvalues
+ std::ranges::join_with_view<View, Pattern> jwv(View{}, Pattern{});
+ assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
+ }
+
+ { // `view` and `pattern` are xvalues
+ View v;
+ Pattern p;
+ std::ranges::join_with_view<View, Pattern> jwv(std::move(v), std::move(p));
+ assert(std::ranges::equal(jwv, std::array{6, 5, 7, 7, 4, 3, 7, 7, 2, 1}));
+ }
+ }
+
+ // Check explicitness
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, View, Pattern>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, View&, Pattern&>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const View, const Pattern>);
+ static_assert(ConstructionIsExplicit<std::ranges::join_with_view<View, Pattern>, const View&, const Pattern&>);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
new file mode 100644
index 0000000000000..8f883f34d0c6e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/end.pass.cpp
@@ -0,0 +1,245 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// constexpr auto end() {
+// if constexpr (forward_range<V> &&
+// is_reference_v<InnerRng> && forward_range<InnerRng> &&
+// common_range<V> && common_range<InnerRng>)
+// return iterator<simple-view<V> && simple-view<Pattern>>{*this, ranges::end(base_)};
+// else
+// return sentinel<simple-view<V> && simple-view<Pattern>>{*this};
+// }
+// constexpr auto end() const
+// requires forward_range<const V> && forward_range<const Pattern> &&
+// is_reference_v<range_reference_t<const V>> &&
+// input_range<range_reference_t<const V>> {
+// using InnerConstRng = range_reference_t<const V>;
+// if constexpr (forward_range<InnerConstRng> &&
+// common_range<const V> && common_range<InnerConstRng>)
+// return iterator<true>{*this, ranges::end(base_)};
+// else
+// return sentinel<true>{*this};
+// }
+
+#include <ranges>
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+template <class V, class Pattern>
+concept JoinWithViewHasConstEnd = requires(const std::ranges::join_with_view<V, Pattern> jwv) { jwv.end(); };
+
+template <size_t Bits>
+ requires(Bits < (1 << 7))
+constexpr void test_end() {
+ constexpr bool v_models_forward_range = Bits & (1 << 0);
+ constexpr bool inner_range_is_reference = Bits & (1 << 1);
+ constexpr bool inner_range_models_forward_range = Bits & (1 << 2);
+ constexpr bool v_models_common_range = Bits & (1 << 3);
+ constexpr bool inner_range_models_common_range = Bits & (1 << 4);
+ constexpr bool v_models_simple_range = Bits & (1 << 5);
+ constexpr bool pattern_models_simple_range = Bits & (1 << 6);
+
+ constexpr ViewProperties inner_range_props{.common = inner_range_models_common_range};
+ using InnerRange =
+ std::conditional_t<inner_range_models_forward_range,
+ BasicView<std::vector<int>, inner_range_props, forward_iterator>,
+ BasicView<std::vector<int>, inner_range_props, DefaultCtorInputIter>>;
+
+ constexpr ViewProperties v_props{.simple = v_models_simple_range, .common = v_models_common_range};
+ using UnderlyingV = std::conditional_t<inner_range_is_reference, std::vector<InnerRange>, RvalueVector<InnerRange>>;
+ using V = std::conditional_t<v_models_forward_range,
+ BasicView<UnderlyingV, v_props, forward_iterator>,
+ BasicView<UnderlyingV, v_props, DefaultCtorInputIter>>;
+
+ using UnderlyingPattern = std::vector<int>;
+ using Pattern = BasicView<UnderlyingPattern, ViewProperties{.simple = pattern_models_simple_range}, forward_iterator>;
+
+ using JWV = std::ranges::join_with_view<V, Pattern>;
+ using Iter = std::ranges::iterator_t<JWV>;
+
+ // Test when `JWV` models common range
+ static_assert(std::same_as<Iter, std::ranges::sentinel_t<JWV>> ==
+ (v_models_forward_range && inner_range_is_reference && inner_range_models_forward_range &&
+ v_models_common_range && inner_range_models_common_range));
+
+ { // `V` and `Pattern` are empty
+ V v{};
+ Pattern pattern{};
+ JWV jwv(std::move(v), std::move(pattern));
+ Iter it = jwv.begin();
+ std::sentinel_for<Iter> decltype(auto) se = jwv.end();
+ assert(it == se);
+ }
+
+ { // `V` is empty, `Pattern` contains some elements
+ V v{};
+ Pattern pattern{std::vector<int>{0}};
+ JWV jwv(std::move(v), std::move(pattern));
+ Iter it = jwv.begin();
+ std::sentinel_for<Iter> decltype(auto) se = jwv.end();
+ assert(it == se);
+ }
+
+ { // `V` is not empty, `Pattern is empty`
+ V v{UnderlyingV{
+ std::vector<InnerRange>{InnerRange(std::vector<int>{1, 2, 3}), InnerRange(std::vector<int>{4, 5, 6})}}};
+ Pattern pattern{};
+ JWV jwv(std::move(v), std::move(pattern));
+ Iter it = jwv.begin();
+ std::sentinel_for<Iter> decltype(auto) se = jwv.end();
+ assert(std::ranges::next(it, 6) == se);
+ }
+
+ { // `V` and `Pattern` are not empty
+ V v{UnderlyingV{std::vector<InnerRange>{
+ InnerRange(std::vector<int>{6, 5}),
+ InnerRange(std::vector<int>{4, 3}),
+ InnerRange(std::vector<int>{2, 1, 0})}}};
+ Pattern pattern{std::vector<int>{-1, -1}};
+ JWV jwv(std::move(v), std::move(pattern));
+ Iter it = jwv.begin();
+ std::sentinel_for<Iter> decltype(auto) se = jwv.end();
+ assert(std::ranges::next(it, 11) == se);
+ }
+}
+
+constexpr void test_end() {}
+
+template <std::size_t Bits>
+ requires(Bits < (1 << 7))
+constexpr void test_const_end() {
+ constexpr bool const_v_models_forward_range = Bits & (1 << 0);
+ constexpr bool const_pattern_models_forward_range = Bits & (1 << 1);
+ constexpr bool inner_const_range_is_reference = Bits & (1 << 2);
+ constexpr bool inner_const_range_models_input_range = Bits & (1 << 3);
+ constexpr bool inner_const_range_models_forward_range = Bits & (1 << 4);
+ constexpr bool const_v_models_common_range = Bits & (1 << 5);
+ constexpr bool inner_const_range_models_common_range = Bits & (1 << 6);
+
+ constexpr ViewProperties inner_range_props{.common = inner_const_range_models_common_range};
+ using InnerRange =
+ std::conditional_t<inner_const_range_models_forward_range,
+ BasicView<std::vector<int>, inner_range_props, forward_iterator>,
+ std::conditional_t<inner_const_range_models_input_range,
+ BasicView<std::vector<int>, inner_range_props, DefaultCtorInputIter>,
+ InputRangeButOutputWhenConst<int>>>;
+
+ constexpr ViewProperties v_props{.common = const_v_models_common_range};
+ using UnderlyingV =
+ std::conditional_t<inner_const_range_is_reference, std::vector<InnerRange>, RvalueVector<InnerRange>>;
+ using V = std::conditional_t<const_v_models_forward_range,
+ BasicView<UnderlyingV, v_props, forward_iterator>,
+ BasicView<UnderlyingV, v_props, DefaultCtorInputIter>>;
+ using Pattern =
+ std::conditional_t<const_pattern_models_forward_range,
+ BasicView<std::vector<int>, ViewProperties{}, forward_iterator>,
+ ForwardViewButInputWhenConst<int>>;
+
+ using JWV = std::ranges::join_with_view<V, Pattern>;
+ static_assert(JoinWithViewHasConstEnd<V, Pattern> ==
+ (const_v_models_forward_range && const_pattern_models_forward_range && inner_const_range_is_reference &&
+ (inner_const_range_models_input_range || inner_const_range_models_forward_range)));
+ static_assert(JoinWithViewHasConstEnd<V, Pattern> == std::ranges::range<const JWV>);
+
+ if constexpr (std::ranges::range<const JWV>) {
+ using ConstIter = std::ranges::iterator_t<const JWV>;
+
+ // Test when `const JWV` models common range
+ static_assert(std::same_as<ConstIter, std::ranges::sentinel_t<const JWV>> ==
+ (inner_const_range_models_forward_range && const_v_models_common_range &&
+ inner_const_range_models_common_range));
+
+ { // `const V` and `const Pattern` are empty
+ V v{};
+ Pattern pattern{};
+ const JWV jwv(std::move(v), std::move(pattern));
+ ConstIter it = jwv.begin();
+ std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
+ assert(it == se);
+ }
+
+ { // `const V` is empty, `const Pattern` contains some elements
+ V v{};
+ Pattern pattern{std::vector<int>{1}};
+ const JWV jwv(std::move(v), std::move(pattern));
+ ConstIter it = jwv.begin();
+ std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
+ assert(it == se);
+ }
+
+ { // `const V` is not empty, `const Pattern is empty`
+ V v{UnderlyingV{
+ std::vector<InnerRange>{InnerRange(std::vector<int>{1, 2, 3}), InnerRange(std::vector<int>{4, 5, 6})}}};
+ Pattern pattern{};
+ const JWV jwv(std::move(v), std::move(pattern));
+ ConstIter it = jwv.begin();
+ std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
+ assert(std::ranges::next(it, 6) == se);
+ }
+
+ { // `const V` and `const Pattern` are not empty
+ V v{UnderlyingV{std::vector<InnerRange>{
+ InnerRange(std::vector<int>{1}), InnerRange(std::vector<int>{2, 2}), InnerRange(std::vector<int>{3, 3, 3})}}};
+ Pattern pattern{std::vector<int>{0}};
+ const JWV jwv(std::move(v), std::move(pattern));
+ ConstIter it = jwv.begin();
+ std::sentinel_for<ConstIter> decltype(auto) se = jwv.end();
+ assert(std::ranges::next(it, 8) == se);
+ }
+ }
+}
+
+constexpr bool test() {
+ []<std::size_t... Bits>(std::index_sequence<Bits...>) {
+ (test_end<Bits>(), ...);
+ (test_const_end<Bits>(), ...);
+ }(std::make_index_sequence<(1 << 7)>{});
+
+ { // Check situation when iterators returned by `end()` and `end() const` are of the same type
+ using V = BasicView<std::vector<std::string>, ViewProperties{.simple = true}, forward_iterator>;
+ using Pattern = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
+ using JWV = std::ranges::join_with_view<V, Pattern>;
+ using Sentinel = std::ranges::sentinel_t<JWV&>;
+ using ConstSentinel = std::ranges::sentinel_t<const JWV&>;
+ static_assert(std::input_iterator<Sentinel>);
+ static_assert(std::input_iterator<ConstSentinel>);
+ static_assert(std::same_as<Sentinel, ConstSentinel>);
+ }
+
+ { // Check situation when sentinels returned by `end()` and `end() const` are of the same type
+ using V = BasicView<std::vector<std::string>, ViewProperties{.simple = true, .common = false}, forward_iterator>;
+ using Pattern = BasicView<std::string, ViewProperties{.simple = true}, forward_iterator>;
+ using JWV = std::ranges::join_with_view<V, Pattern>;
+ using Sentinel = std::ranges::sentinel_t<JWV&>;
+ using ConstSentinel = std::ranges::sentinel_t<const JWV&>;
+ static_assert(!std::input_iterator<Sentinel>);
+ static_assert(!std::input_iterator<ConstSentinel>);
+ static_assert(std::same_as<Sentinel, ConstSentinel>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/inheritance.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/inheritance.compile.pass.cpp
new file mode 100644
index 0000000000000..ef9522a448a21
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.view/inheritance.compile.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// class join_with_view : public view_interface<join_with_view<V, Pattern>>
+
+#include <ranges>
+
+#include <concepts>
+#include <string>
+#include <vector>
+
+template <class T>
+struct View : std::ranges::view_base {
+ std::vector<T>* begin();
+ std::vector<T>* end();
+};
+
+template <class T>
+struct Pattern : std::ranges::view_base {
+ T* begin();
+ T* end();
+};
+
+template <class T>
+using JoinWithView = std::ranges::join_with_view<View<T>, Pattern<T>>;
+
+static_assert(std::derived_from<JoinWithView<int>, std::ranges::view_interface<JoinWithView<int>>>);
+static_assert(std::derived_from<JoinWithView<void*>, std::ranges::view_interface<JoinWithView<void*>>>);
+static_assert(std::derived_from<JoinWithView<std::string>, std::ranges::view_interface<JoinWithView<std::string>>>);
>From 6444d0450f74e9fd6b2e95a8bcfce1e81c85488c Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:49:48 +0100
Subject: [PATCH 05/16] Test `[range.join.with.sentinel]`
---
.../ctor.default.pass.cpp | 38 ++++++
.../ctor.non_const.pass.cpp | 72 +++++++++++
.../range.join.with.sentinel/eq.pass.cpp | 112 ++++++++++++++++++
3 files changed, 222 insertions(+)
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.non_const.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.pass.cpp
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..55f6603e34960
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.default.pass.cpp
@@ -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
+// UNSUPPORTED: !c++experimental
+
+// <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;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.non_const.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.non_const.pass.cpp
new file mode 100644
index 0000000000000..e47dc66d5d639
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/ctor.non_const.pass.cpp
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.pass.cpp
new file mode 100644
index 0000000000000..30a4aa058842e
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.pass.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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>);
+
+template <class T, class U>
+concept weakly_equality_comparable_with = requires(const T& t, const U& u) {
+ t == u;
+ t != u;
+ u == t;
+ u != t;
+};
+
+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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
>From 634d9b47986b42a5cb1e0b3513ef25c0931994d1 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:50:18 +0100
Subject: [PATCH 06/16] Test `[range.join.with.iterator]`
---
.../ctor.default.pass.cpp | 80 +++
.../ctor.not_const.pass.cpp | 107 ++++
.../decrement.pass.cpp | 284 +++++++++++
.../range.join.with.iterator/deref.pass.cpp | 147 ++++++
.../range.join.with.iterator/eq.pass.cpp | 259 ++++++++++
.../increment.pass.cpp | 317 ++++++++++++
.../iter_move.pass.cpp | 343 +++++++++++++
.../iter_swap.pass.cpp | 84 ++++
.../types.compile.pass.cpp | 457 ++++++++++++++++++
9 files changed, 2078 insertions(+)
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp
new file mode 100644
index 0000000000000..b1a9288abb518
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.default.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp
new file mode 100644
index 0000000000000..0ffb43e427332
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/ctor.not_const.pass.cpp
@@ -0,0 +1,107 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp
new file mode 100644
index 0000000000000..0465ef8059a82
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/decrement.pass.cpp
@@ -0,0 +1,284 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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());
+ }
+ }
+
+ { // `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);
+ }
+ }
+
+ { // `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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
new file mode 100644
index 0000000000000..b78052d3f4305
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.pass.cpp
@@ -0,0 +1,147 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp
new file mode 100644
index 0000000000000..e6930cfe624c9
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.pass.cpp
@@ -0,0 +1,259 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp
new file mode 100644
index 0000000000000..479eedf47e877
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/increment.pass.cpp
@@ -0,0 +1,317 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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);
+ }
+ }
+
+ { // `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());
+ }
+ }
+
+ { // 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++)>);
+ }
+
+ { // 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++)>);
+ }
+}
+
+constexpr bool test() {
+ test_pre_increment();
+ test_post_increment();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
new file mode 100644
index 0000000000000..7021c58ba4b44
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.pass.cpp
@@ -0,0 +1,343 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
new file mode 100644
index 0000000000000..caf1144581088
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_swap.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp
new file mode 100644
index 0000000000000..b4188f4d74df1
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.iterator/types.compile.pass.cpp
@@ -0,0 +1,457 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <ranges>
+
+// using iterator_concept = see below;
+// using iterator_category = see below; // not always present
+// using value_type = see below;
+// using difference_type = see below;
+
+#include <ranges>
+
+#include <iterator>
+#include <vector>
+
+#include "../types.h"
+#include "test_iterators.h"
+
+namespace test_iterator_concept {
+template <template <class> class InnerIt>
+using InnerRange = BasicView<std::vector<int>, ViewProperties{}, InnerIt>;
+
+template <template <class> class It, template <class> class InnerIt>
+using View = BasicView<std::vector<InnerRange<InnerIt>>, ViewProperties{}, It>;
+
+template <template <class> class It, template <class> class InnerIt>
+using RvalueView = BasicView<RvalueVector<InnerRange<InnerIt>>, ViewProperties{}, It>;
+
+template <template <class> class It>
+using Pattern = BasicView<std::vector<int>, ViewProperties{}, It>;
+
+template <class V, class Pat>
+using IteratorConcept = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::iterator_concept;
+
+template <class V, class Pat, class Concept>
+concept IteratorConceptIs = std::same_as<IteratorConcept<V, Pat>, Concept>;
+
+// When `iterator<false>::iterator_concept` is `bidirectional_iterator_tag`
+static_assert(IteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::bidirectional_iterator_tag>);
+
+// When `iterator<false>::iterator_concept` is `forward_iterator_tag`
+static_assert(IteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
+ Pattern<forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<forward_iterator, forward_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
+ Pattern<forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
+ Pattern<forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorConceptIs<View<forward_iterator, forward_iterator>, //
+ Pattern<forward_iterator>,
+ std::forward_iterator_tag>);
+
+// When `iterator<false>::iterator_concept` is `input_iterator_tag`
+static_assert(IteratorConceptIs<View<DefaultCtorInputIter, forward_iterator>,
+ Pattern<forward_iterator>,
+ std::input_iterator_tag>);
+static_assert(IteratorConceptIs<View<forward_iterator, DefaultCtorInputIter>,
+ Pattern<forward_iterator>,
+ std::input_iterator_tag>);
+static_assert(IteratorConceptIs<View<DefaultCtorInputIter, DefaultCtorInputIter>,
+ Pattern<forward_iterator>,
+ std::input_iterator_tag>);
+static_assert(IteratorConceptIs<RvalueView<bidirectional_iterator, bidirectional_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::input_iterator_tag>);
+static_assert(IteratorConceptIs<RvalueView<forward_iterator, forward_iterator>,
+ Pattern<forward_iterator>,
+ std::input_iterator_tag>);
+
+template <class V, class Pat>
+using ConstIteratorConcept = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::iterator_concept;
+
+template <class V, class Pat, class Concept>
+concept ConstIteratorConceptIs = std::same_as<ConstIteratorConcept<V, Pat>, Concept>;
+
+// When `iterator<true>::iterator_concept` is `bidirectional_iterator_tag`
+static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::bidirectional_iterator_tag>);
+
+// When `iterator<true>::iterator_concept` is `forward_iterator_tag`
+static_assert(ConstIteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, bidirectional_iterator>,
+ Pattern<forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<forward_iterator, forward_iterator>,
+ Pattern<bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<forward_iterator, bidirectional_iterator>,
+ Pattern<forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<bidirectional_iterator, forward_iterator>,
+ Pattern<forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorConceptIs<View<forward_iterator, forward_iterator>,
+ Pattern<forward_iterator>,
+ std::forward_iterator_tag>);
+
+// `iterator<true>::iterator_concept` cannot be `input_iterator_tag`
+} // namespace test_iterator_concept
+
+namespace test_iterator_category {
+template <template <class> class InnerIt>
+using InnerRange = BasicView<std::vector<float>, ViewProperties{}, InnerIt>;
+
+template <bool Common, template <class> class InnerIt>
+using MaybeCommonInnerRange = BasicView<std::vector<float>, ViewProperties{.common = Common}, InnerIt>;
+
+template <template <class> class It, template <class> class InnerIt>
+using View = BasicView<std::vector<InnerRange<InnerIt>>, ViewProperties{}, It>;
+
+template <template <class> class It, template <class> class InnerIt>
+using RvalueView = BasicView<RvalueVector<InnerRange<InnerIt>>, ViewProperties{}, It>;
+
+template <bool Common, template <class> class It, bool CommonInner, template <class> class InnerIt>
+using MaybeCommonView =
+ BasicView<std::vector<MaybeCommonInnerRange<CommonInner, InnerIt>>, ViewProperties{.common = Common}, It>;
+
+template <template <class> class It>
+using Pattern = BasicView<std::vector<float>, ViewProperties{}, It>;
+
+template <template <class> class It>
+using RvaluePattern = BasicView<RvalueVector<float>, ViewProperties{}, It>;
+
+template <bool Common, template <class> class It>
+using MaybeCommonPattern = BasicView<std::vector<float>, ViewProperties{.common = Common}, It>;
+
+template <class V, class Pattern>
+using IteratorCategory = std::ranges::iterator_t<std::ranges::join_with_view<V, Pattern>>::iterator_category;
+
+template <class V, class Pattern>
+concept HasIteratorCategory = requires { typename IteratorCategory<V, Pattern>; };
+
+template <class V, class Pat, class Category>
+concept IteratorCategoryIs = std::same_as<IteratorCategory<V, Pat>, Category>;
+
+// When `iterator<false>::iterator_category` is not defined
+static_assert(!HasIteratorCategory<View<cpp20_input_iterator, forward_iterator>, Pattern<forward_iterator>>);
+static_assert(!HasIteratorCategory<View<forward_iterator, cpp20_input_iterator>, Pattern<forward_iterator>>);
+static_assert(!HasIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<cpp20_input_iterator>>);
+static_assert(!HasIteratorCategory<RvalueView<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
+static_assert(HasIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
+
+// When
+// is_reference_v<common_reference_t<iter_reference_t<InnerIter>,
+// iter_reference_t<PatternIter>>>
+// has different values for `iterator<false>`
+static_assert(IteratorCategoryIs<View<forward_iterator, forward_iterator>,
+ RvaluePattern<forward_iterator>,
+ std::input_iterator_tag>);
+
+// When `iterator<false>::iterator_category` is `bidirectional_iterator_tag`
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::bidirectional_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<false, bidirectional_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::bidirectional_iterator_tag>);
+
+// When `iterator<false>::iterator_category` is `forward_iterator_tag`
+static_assert(IteratorCategoryIs<MaybeCommonView<true, forward_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, forward_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<true, forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, false, bidirectional_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<false, bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(IteratorCategoryIs<MaybeCommonView<false, forward_iterator, false, forward_iterator>,
+ MaybeCommonPattern<false, forward_iterator>,
+ std::forward_iterator_tag>);
+
+// When `iterator<false>::iterator_category` is `input_iterator_tag`
+static_assert(IteratorCategoryIs<View<ForwardIteratorWithInputCategory, forward_iterator>,
+ Pattern<forward_iterator>,
+ std::input_iterator_tag>);
+static_assert(IteratorCategoryIs<View<forward_iterator, ForwardIteratorWithInputCategory>,
+ Pattern<forward_iterator>,
+ std::input_iterator_tag>);
+static_assert(IteratorCategoryIs<View<forward_iterator, forward_iterator>,
+ Pattern<ForwardIteratorWithInputCategory>,
+ std::input_iterator_tag>);
+static_assert(IteratorCategoryIs<View<ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory>,
+ Pattern<ForwardIteratorWithInputCategory>,
+ std::input_iterator_tag>);
+
+template <class V, class Pattern>
+using ConstIteratorCategory = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pattern>>::iterator_category;
+
+template <class V, class Pattern>
+concept HasConstIteratorCategory = requires { typename ConstIteratorCategory<V, Pattern>; };
+
+template <class V, class Pat, class Category>
+concept ConstIteratorCategoryIs = std::same_as<ConstIteratorCategory<V, Pat>, Category>;
+
+// `iterator<true>::iterator_category` is not defined in those
+// cases because `join_with_view<V, Pattern>` cannot const-accessed
+static_assert(!HasConstIteratorCategory<View<cpp20_input_iterator, forward_iterator>, Pattern<forward_iterator>>);
+static_assert(!HasConstIteratorCategory<View<forward_iterator, cpp20_input_iterator>, Pattern<forward_iterator>>);
+static_assert(!HasConstIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<cpp20_input_iterator>>);
+static_assert(!HasConstIteratorCategory<RvalueView<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
+static_assert(HasConstIteratorCategory<View<forward_iterator, forward_iterator>, Pattern<forward_iterator>>);
+
+// When
+// is_reference_v<common_reference_t<iter_reference_t<InnerIter>,
+// iter_reference_t<PatternIter>>>
+// has different values for `iterator<true>`
+static_assert(ConstIteratorCategoryIs<View<forward_iterator, forward_iterator>,
+ RvaluePattern<forward_iterator>,
+ std::input_iterator_tag>);
+
+// When `iterator<true>::iterator_category` is `bidirectional_iterator_tag`
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::bidirectional_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<false, bidirectional_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::bidirectional_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<
+ BasicVectorView<
+ BasicVectorView<float, ViewProperties{.common = true}, forward_iterator, bidirectional_iterator>,
+ ViewProperties{.common = true},
+ forward_iterator,
+ bidirectional_iterator>,
+ BasicVectorView<float, ViewProperties{.common = true}, forward_iterator, bidirectional_iterator>,
+ std::bidirectional_iterator_tag>);
+
+// When `iterator<true>::iterator_category` is `forward_iterator_tag`
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, forward_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, forward_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<true, forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, false, bidirectional_iterator>,
+ MaybeCommonPattern<true, bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<true, bidirectional_iterator, true, bidirectional_iterator>,
+ MaybeCommonPattern<false, bidirectional_iterator>,
+ std::forward_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<MaybeCommonView<false, forward_iterator, false, forward_iterator>,
+ MaybeCommonPattern<false, forward_iterator>,
+ std::forward_iterator_tag>);
+static_assert(
+ ConstIteratorCategoryIs<
+ BasicVectorView<BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory, forward_iterator>,
+ ViewProperties{},
+ ForwardIteratorWithInputCategory,
+ forward_iterator>,
+ BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory, forward_iterator>,
+ std::forward_iterator_tag>);
+
+// When `iterator<true>::iterator_category` is `input_iterator_tag`
+static_assert(ConstIteratorCategoryIs<View<ForwardIteratorWithInputCategory, forward_iterator>,
+ Pattern<forward_iterator>,
+ std::input_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<View<forward_iterator, ForwardIteratorWithInputCategory>,
+ Pattern<forward_iterator>,
+ std::input_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<View<forward_iterator, forward_iterator>,
+ Pattern<ForwardIteratorWithInputCategory>,
+ std::input_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<View<ForwardIteratorWithInputCategory, ForwardIteratorWithInputCategory>,
+ Pattern<ForwardIteratorWithInputCategory>,
+ std::input_iterator_tag>);
+static_assert(ConstIteratorCategoryIs<
+ BasicVectorView<
+ BasicVectorView<float, ViewProperties{}, DefaultCtorInputIter, ForwardIteratorWithInputCategory>,
+ ViewProperties{},
+ DefaultCtorInputIter,
+ ForwardIteratorWithInputCategory>,
+ BasicVectorView<float, ViewProperties{}, ForwardIteratorWithInputCategory>,
+ std::input_iterator_tag>);
+} // namespace test_iterator_category
+
+namespace test_value_type {
+template <class ValueType, class ConstValueType = ValueType>
+struct View : std::ranges::view_base {
+ struct InnerRange : std::ranges::view_base {
+ ValueType* begin();
+ ValueType* end();
+ ConstValueType* begin() const;
+ ConstValueType* end() const;
+ };
+
+ InnerRange* begin();
+ InnerRange* end();
+ const InnerRange* begin() const;
+ const InnerRange* end() const;
+};
+
+template <class ValueType, class ConstValueType = ValueType>
+using Pattern = View<ValueType, ConstValueType>::InnerRange;
+
+template <class V, class Pat>
+using IteratorValueType = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::value_type;
+
+template <class V, class Pat, class ValueType>
+concept IteratorValueTypeIs = std::same_as<IteratorValueType<V, Pat>, ValueType>;
+
+// Test that `iterator<false>::value_type` is equal to
+// common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>
+static_assert(IteratorValueTypeIs<View<int>, Pattern<int>, int>);
+static_assert(IteratorValueTypeIs<View<int>, Pattern<long>, long>);
+static_assert(IteratorValueTypeIs<View<long>, Pattern<int>, long>);
+static_assert(IteratorValueTypeIs<View<std::nullptr_t>, Pattern<void*>, void*>);
+static_assert(IteratorValueTypeIs<View<std::tuple<long, int>>, Pattern<std::tuple<int, long>>, std::tuple<long, long>>);
+
+template <class V, class Pat>
+using ConstIteratorValueType = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::value_type;
+
+template <class V, class Pat, class ValueType>
+concept ConstIteratorValueTypeIs = std::same_as<ConstIteratorValueType<V, Pat>, ValueType>;
+
+// Test that `iterator<true>::value_type` is equal to
+// common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>
+static_assert(ConstIteratorValueTypeIs<View<int>, Pattern<int>, int>);
+static_assert(ConstIteratorValueTypeIs<View<int>, Pattern<long>, long>);
+static_assert(ConstIteratorValueTypeIs<View<long>, Pattern<int>, long>);
+static_assert(ConstIteratorValueTypeIs<View<std::nullptr_t>, Pattern<void*>, void*>);
+static_assert(
+ ConstIteratorValueTypeIs<View<std::tuple<long, int>>, Pattern<std::tuple<int, long>>, std::tuple<long, long>>);
+
+// Test value types of non-simple const ranges
+static_assert(ConstIteratorValueTypeIs<View<short, int>, Pattern<short, int>, int>);
+static_assert(ConstIteratorValueTypeIs<View<short, int>, Pattern<int, long>, long>);
+static_assert(ConstIteratorValueTypeIs<View<int, long>, Pattern<short, int>, long>);
+static_assert(ConstIteratorValueTypeIs<View<int, std::nullptr_t>, Pattern<int, void*>, void*>);
+static_assert(ConstIteratorValueTypeIs<View<std::tuple<long, int>, std::pair<long, int>>,
+ Pattern<std::tuple<int, long>, std::pair<int, long>>,
+ std::pair<long, long>>);
+} // namespace test_value_type
+
+namespace test_difference_type {
+template <class DifferenceType, class ValueType>
+struct Iter {
+ using value_type = std::remove_const_t<ValueType>;
+ using difference_type = DifferenceType;
+
+ ValueType& operator*() const;
+ Iter& operator++();
+ Iter operator++(int);
+ friend bool operator==(const Iter&, const Iter&);
+};
+
+static_assert(std::forward_iterator<Iter<int, void*>>);
+
+template <class DifferenceType,
+ class InnerDifferenceType,
+ class ConstDifferenceType = DifferenceType,
+ class InnerConstDifferenceType = InnerDifferenceType>
+struct View : std::ranges::view_base {
+ struct InnerRange : std::ranges::view_base {
+ Iter<InnerDifferenceType, float> begin();
+ Iter<InnerDifferenceType, float> end();
+ Iter<InnerConstDifferenceType, double> begin() const;
+ Iter<InnerConstDifferenceType, double> end() const;
+ };
+
+ Iter<DifferenceType, InnerRange> begin();
+ Iter<DifferenceType, InnerRange> end();
+ Iter<ConstDifferenceType, const InnerRange> begin() const;
+ Iter<ConstDifferenceType, const InnerRange> end() const;
+};
+
+template <class DifferenceType, class ConstDifferenceType = DifferenceType>
+struct Pattern : std::ranges::view_base {
+ Iter<DifferenceType, float> begin();
+ Iter<DifferenceType, float> end();
+ Iter<ConstDifferenceType, double> begin() const;
+ Iter<ConstDifferenceType, double> end() const;
+};
+
+template <class V, class Pat>
+using IteratorDifferenceType = std::ranges::iterator_t<std::ranges::join_with_view<V, Pat>>::difference_type;
+
+template <class V, class Pat, class DifferenceType>
+concept IteratorDifferenceTypeIs = std::same_as<IteratorDifferenceType<V, Pat>, DifferenceType>;
+
+// Test that `iterator<false>::difference_type` is equal to
+// common_type_t<
+// iter_difference_t<OuterIter>,
+// iter_difference_t<InnerIter>,
+// iter_difference_t<PatternIter>>
+static_assert(IteratorDifferenceTypeIs<View<int, int>, Pattern<int>, int>);
+static_assert(IteratorDifferenceTypeIs<View<signed char, signed char>, Pattern<signed char>, signed char>);
+static_assert(IteratorDifferenceTypeIs<View<short, short>, Pattern<short>, short>);
+static_assert(IteratorDifferenceTypeIs<View<signed char, short>, Pattern<short>, int>);
+static_assert(IteratorDifferenceTypeIs<View<signed char, short>, Pattern<int>, int>);
+static_assert(IteratorDifferenceTypeIs<View<long long, long>, Pattern<int>, long long>);
+static_assert(IteratorDifferenceTypeIs<View<long, long long>, Pattern<int>, long long>);
+
+template <class V, class Pat>
+using ConstIteratorDifferenceType = std::ranges::iterator_t<const std::ranges::join_with_view<V, Pat>>::difference_type;
+
+template <class V, class Pat, class DifferenceType>
+concept ConstIteratorDifferenceTypeIs = std::same_as<ConstIteratorDifferenceType<V, Pat>, DifferenceType>;
+
+// Test that `iterator<true>::difference_type` is equal to
+// common_type_t<
+// iter_difference_t<OuterIter>,
+// iter_difference_t<InnerIter>,
+// iter_difference_t<PatternIter>>
+static_assert(ConstIteratorDifferenceTypeIs<View<int, int>, Pattern<int>, int>);
+static_assert(ConstIteratorDifferenceTypeIs<View<signed char, signed char>, Pattern<signed char>, signed char>);
+static_assert(ConstIteratorDifferenceTypeIs<View<short, short>, Pattern<short>, short>);
+static_assert(ConstIteratorDifferenceTypeIs<View<signed char, short>, Pattern<short>, int>);
+static_assert(ConstIteratorDifferenceTypeIs<View<signed char, short>, Pattern<int>, int>);
+static_assert(ConstIteratorDifferenceTypeIs<View<long long, long>, Pattern<int>, long long>);
+static_assert(ConstIteratorDifferenceTypeIs<View<long, long long>, Pattern<int>, long long>);
+
+// Test difference types of non-simple const ranges
+static_assert(ConstIteratorDifferenceTypeIs<View<short, short, int, int>, Pattern<short, int>, int>);
+static_assert(
+ ConstIteratorDifferenceTypeIs<View<int, short, signed char, signed char>, Pattern<long, signed char>, signed char>);
+static_assert(ConstIteratorDifferenceTypeIs<View<long, long long, signed char, short>, Pattern<long, short>, int>);
+static_assert(ConstIteratorDifferenceTypeIs<View<short, short, long long, long>, Pattern<short, int>, long long>);
+static_assert(ConstIteratorDifferenceTypeIs<View<signed char, signed char, long, long long>,
+ Pattern<signed char, int>,
+ long long>);
+} // namespace test_difference_type
>From 5b539d298449cab8cf96b6eb868eb76d01688825 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:51:11 +0100
Subject: [PATCH 07/16] Test `[range.join.with.overview]`
---
.../range.join.with.overview/adaptor.pass.cpp | 362 ++++++++++++++++++
.../range.join.with.overview/example.pass.cpp | 48 +++
2 files changed, 410 insertions(+)
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
create mode 100644 libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
new file mode 100644
index 0000000000000..ae7ccdd667168
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.pass.cpp
@@ -0,0 +1,362 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
new file mode 100644
index 0000000000000..97d1c5667db1f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// UNSUPPORTED: !c++experimental
+
+// <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();
+#if __cpp_lib_variant >= 202106
+ static_assert(test());
+#endif
+
+ return 0;
+}
>From 575b46e7d15f10b6ad1c49cf11495923edb53f0c Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:51:32 +0100
Subject: [PATCH 08/16] Test `[[nodiscard]]` extensions
---
.../deref.nodiscard.verify.cpp | 28 +++++++++++++++
.../eq.nodiscard.verify.cpp | 30 ++++++++++++++++
.../iter_move.nodiscard.verify.cpp | 28 +++++++++++++++
.../adaptor.nodiscard.verify.cpp | 33 +++++++++++++++++
.../eq.nodiscard.verify.cpp | 35 +++++++++++++++++++
.../base.nodiscard.verify.cpp | 30 ++++++++++++++++
.../begin.nodiscard.verify.cpp | 28 +++++++++++++++
.../end.nodiscard.verify.cpp | 28 +++++++++++++++
8 files changed, 240 insertions(+)
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp
create mode 100644 libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..58abf717d7bbe
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/deref.nodiscard.verify.cpp
@@ -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
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..78b6332f778e1
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/eq.nodiscard.verify.cpp
@@ -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
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..1e4a92f827572
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.iterator/iter_move.nodiscard.verify.cpp
@@ -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
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..bd2972f938648
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.overview/adaptor.nodiscard.verify.cpp
@@ -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
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..8cd234b85ad1d
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.sentinel/eq.nodiscard.verify.cpp
@@ -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
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..b08b9f9b8bc0d
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/base.nodiscard.verify.cpp
@@ -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
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..6d930d46be77a
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/begin.nodiscard.verify.cpp
@@ -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
+}
diff --git a/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp
new file mode 100644
index 0000000000000..1d5f0e394d67f
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/range.adaptors/range.join.with/range.join.with.view/end.nodiscard.verify.cpp
@@ -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
+}
>From 1835f687f10e348221ea283e343a2bd38ababd2f Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:52:59 +0100
Subject: [PATCH 09/16] Update docs
---
libcxx/docs/ReleaseNotes/19.rst | 3 +++
libcxx/docs/Status/Cxx23.rst | 5 ++---
libcxx/docs/Status/Cxx23Papers.csv | 6 +++---
libcxx/docs/Status/RangesViews.csv | 2 +-
libcxx/docs/UsingLibcxx.rst | 1 +
5 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index db731de2e4399..19a2a48669dd1 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -40,6 +40,9 @@ Implemented Papers
- P2637R3 - Member ``visit``
- P2652R2 - Disallow User Specialization of ``allocator_traits``
+- P2441R2 - ``views::join_with`` (in the experimental library)
+- P2711R1 - Making multi-param constructors of ``views`` ``explicit``
+- P2770R0 - Stashing stashing ``iterators`` for proper flattening
Improvements and New Features
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index 3e6e33f08c7cc..55fecdc7d0a5a 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -44,8 +44,7 @@ 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-P2441R2] P2441R2: ``views::join_with`` cannot be used in constant expressions (depends on P2231R1).
.. [#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,4 +62,4 @@ 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.
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb415ed8c031f..91914b8ed2c75 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -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","","","|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","|Partial| [#note-P2441R2]_","","|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","","","|format|"
"`P2675R1 <https://wg21.link/P2675R1>`__","LWG", "``format``'s width estimation is too approximate and not forward compatible","February 2023","|Complete|","17.0","|format|"
diff --git a/libcxx/docs/Status/RangesViews.csv b/libcxx/docs/Status/RangesViews.csv
index f141656eb131a..610c5b76368e5 100644
--- a/libcxx/docs/Status/RangesViews.csv
+++ b/libcxx/docs/Status/RangesViews.csv
@@ -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>`_,In Progress
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>`_,✅
diff --git a/libcxx/docs/UsingLibcxx.rst b/libcxx/docs/UsingLibcxx.rst
index 3b1be286c1698..d7f7ae67347b8 100644
--- a/libcxx/docs/UsingLibcxx.rst
+++ b/libcxx/docs/UsingLibcxx.rst
@@ -50,6 +50,7 @@ when ``-fexperimental-library`` is passed:
* ``std::stop_token``, ``std::stop_source`` and ``std::stop_callback``
* ``std::jthread``
* ``std::chrono::tzdb`` and related time zone functionality
+* ``std::ranges::join_with_view``
.. warning::
Experimental libraries are experimental.
>From 07c91315c617b8749b04c048e8608815bcb6083d Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 00:59:58 +0100
Subject: [PATCH 10/16] Update `RangesMajorFeatures.csv`
---
libcxx/docs/Status/RangesMajorFeatures.csv | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/docs/Status/RangesMajorFeatures.csv b/libcxx/docs/Status/RangesMajorFeatures.csv
index c0bec8d924e8a..76cd564ff5ab2 100644
--- a/libcxx/docs/Status/RangesMajorFeatures.csv
+++ b/libcxx/docs/Status/RangesMajorFeatures.csv
@@ -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>`_,Unassigned,No patch yet,Not started
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
>From de59d3677f6bb4c851905705442a311fd899aace Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 01:04:37 +0100
Subject: [PATCH 11/16] Fix formatting
---
libcxx/modules/std/ranges.inc | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index 399d8983b4d08..c899d755a10b7 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -122,7 +122,7 @@ export namespace std {
namespace views {
using std::ranges::views::repeat;
- } // namespace views
+ } // namespace views
#endif // _LIBCPP_STD_VER >= 23
#ifndef _LIBCPP_HAS_NO_LOCALIZATION
@@ -273,7 +273,7 @@ export namespace std {
namespace views {
using std::ranges::views::zip;
- } // namespace views
+ } // namespace views
#endif // _LIBCPP_STD_VER >= 23
#if 0
>From 01ea1f811eb642e9e6d2ed87bec28145879c5aa1 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 01:05:28 +0100
Subject: [PATCH 12/16] Add `// [range.join.with]` in `ranges.inc`
---
libcxx/modules/std/ranges.inc | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index c899d755a10b7..cd67d0699569f 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -211,6 +211,7 @@ export namespace std {
} // namespace views
#if _LIBCPP_STD_VER >= 23 && defined(_LIBCPP_ENABLE_EXPERIMENTAL)
+ // [range.join.with]
using std::ranges::join_with_view;
namespace views {
>From f90b842e727c1f6445dd3060d7337dcf1960f3df Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 01:09:48 +0100
Subject: [PATCH 13/16] Run `libcxx-generate-files`
---
libcxx/include/libcxx.imp | 1 +
1 file changed, 1 insertion(+)
diff --git a/libcxx/include/libcxx.imp b/libcxx/include/libcxx.imp
index 13c0dfeb2bc98..4373a525dd2f4 100644
--- a/libcxx/include/libcxx.imp
+++ b/libcxx/include/libcxx.imp
@@ -622,6 +622,7 @@
{ include: [ "<__ranges/iota_view.h>", "private", "<ranges>", "public" ] },
{ include: [ "<__ranges/istream_view.h>", "private", "<ranges>", "public" ] },
{ include: [ "<__ranges/join_view.h>", "private", "<ranges>", "public" ] },
+ { include: [ "<__ranges/join_with_view.h>", "private", "<ranges>", "public" ] },
{ include: [ "<__ranges/lazy_split_view.h>", "private", "<ranges>", "public" ] },
{ include: [ "<__ranges/movable_box.h>", "private", "<ranges>", "public" ] },
{ include: [ "<__ranges/non_propagating_cache.h>", "private", "<ranges>", "public" ] },
>From 95c99e454863f2e5eb1c00e675a86f8e9f75c205 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 01:16:43 +0100
Subject: [PATCH 14/16] Cleanup `ForwardIteratorWithInputCategory`
---
.../std/ranges/range.adaptors/range.join.with/types.h | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
index cdbc404bfd789..0f5a36425c335 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
@@ -149,8 +149,7 @@ using ForwardViewButInputWhenConst =
template <class It>
class ForwardIteratorWithInputCategory {
- using Self = ForwardIteratorWithInputCategory;
- It it_ = It();
+ It it_ = It();
public:
using value_type = std::iter_value_t<It>;
@@ -162,9 +161,9 @@ class ForwardIteratorWithInputCategory {
explicit ForwardIteratorWithInputCategory(It it);
std::iter_reference_t<It> operator*() const;
- Self& operator++();
- Self operator++(int);
- friend bool operator==(const Self&, const Self&);
+ ForwardIteratorWithInputCategory& operator++();
+ ForwardIteratorWithInputCategory operator++(int);
+ friend bool operator==(const ForwardIteratorWithInputCategory&, const ForwardIteratorWithInputCategory&);
};
template <class It>
>From ae39c5bf77d132874d749b87e675711de4614a72 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 01:33:54 +0100
Subject: [PATCH 15/16] =?UTF-8?q?Minor=20fix=20(`=E2=80=94`=20->=20`-`)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../range.join.with/range.join.with.overview/example.pass.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
index 97d1c5667db1f..8b261d8ba2d95 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/range.join.with.overview/example.pass.cpp
@@ -17,7 +17,7 @@
// cout << c;
// }
// // The above prints the-quick-brown-fox
-// — end example]
+// - end example]
#include <ranges>
>From 7194c70a67ee15af312aac810b9c2d4ff35e27f7 Mon Sep 17 00:00:00 2001
From: Jakub Mazurkiewicz <mazkuba3 at gmail.com>
Date: Sat, 3 Feb 2024 01:35:40 +0100
Subject: [PATCH 16/16] GCC-13: Fix
`ForwardIteratorWithInputCategory::operator==`
---
libcxx/test/std/ranges/range.adaptors/range.join.with/types.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
index 0f5a36425c335..be55120296682 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
+++ b/libcxx/test/std/ranges/range.adaptors/range.join.with/types.h
@@ -163,7 +163,7 @@ class ForwardIteratorWithInputCategory {
std::iter_reference_t<It> operator*() const;
ForwardIteratorWithInputCategory& operator++();
ForwardIteratorWithInputCategory operator++(int);
- friend bool operator==(const ForwardIteratorWithInputCategory&, const ForwardIteratorWithInputCategory&);
+ bool operator==(const ForwardIteratorWithInputCategory&) const;
};
template <class It>
More information about the libcxx-commits
mailing list