diff --git a/libcxx/docs/Status/RangesPaper.csv b/libcxx/docs/Status/RangesPaper.csv --- a/libcxx/docs/Status/RangesPaper.csv +++ b/libcxx/docs/Status/RangesPaper.csv @@ -140,7 +140,7 @@ `[range.transform] `_,`transform_view `_,[range.all],Zoe Carver,✅ `[range.iota] `_,iota_view,[range.all],Zoe Carver,✅ `[range.take] `_,take_view,[range.all],Zoe Carver,✅ -`[range.join] `_,join_view,[range.all],Zoe Carver,In Progress +`[range.join] `_,join_view,[range.all],Zoe Carver,✅ `[range.empty] `_,`empty_view `_,[view.interface],Zoe Carver,✅ `[range.single] `_,single_view,[view.interface],Zoe Carver,✅ `[range.split] `_,split_view,[range.all],Zoe Carver,In Progress diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -224,6 +224,7 @@ __ranges/enable_borrowed_range.h __ranges/enable_view.h __ranges/iota_view.h + __ranges/join_view.h __ranges/non_propagating_cache.h __ranges/ref_view.h __ranges/reverse_view.h diff --git a/libcxx/include/__ranges/join_view.h b/libcxx/include/__ranges/join_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/join_view.h @@ -0,0 +1,350 @@ +// -*- 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_VIEW_H +#define _LIBCPP___RANGES_JOIN_VIEW_H + +#include <__config> +#include <__iterator/concepts.h> +#include <__iterator/iterator_traits.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/ref_view.h> +#include <__ranges/subrange.h> +#include <__ranges/view_interface.h> +#include <__utility/declval.h> +#include <__utility/forward.h> +#include +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_RANGES) + +namespace ranges { + template + struct __join_view_iterator_category {}; + + template + requires is_reference_v> && + forward_range<_View> && + forward_range> + struct __join_view_iterator_category<_View> { + using _OuterC = typename iterator_traits>::iterator_category; + using _InnerC = typename iterator_traits>>::iterator_category; + + using iterator_category = _If< + derived_from<_OuterC, bidirectional_iterator_tag> && derived_from<_InnerC, bidirectional_iterator_tag>, + bidirectional_iterator_tag, + _If< + derived_from<_OuterC, forward_iterator_tag> && derived_from<_InnerC, forward_iterator_tag>, + forward_iterator_tag, + input_iterator_tag + > + >; + }; + + template + requires view<_View> && input_range> + class join_view + : public view_interface> { + private: + using _InnerRange = range_reference_t<_View>; + + template struct __iterator; + template struct __sentinel; + + static constexpr bool _UseCache = !is_reference_v<_InnerRange>; + using _Cache = _If<_UseCache, __non_propagating_cache>, __empty_cache>; + [[no_unique_address]] _Cache __cache_; + _View __base_ = _View(); // TODO: [[no_unique_address]] makes clang crash! File a bug :) + + public: + _LIBCPP_HIDE_FROM_ABI + join_view() requires default_initializable<_View> = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit join_view(_View __base) + : __base_(_VSTD::move(__base)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr _View base() const& requires copy_constructible<_View> { return __base_; } + + _LIBCPP_HIDE_FROM_ABI + constexpr _View base() && { return _VSTD::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() { + constexpr bool __use_const = __simple_view<_View> && + is_reference_v>; + return __iterator<__use_const>{*this, ranges::begin(__base_)}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() const + requires input_range && + is_reference_v> + { + return __iterator{*this, ranges::begin(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() { + if constexpr (forward_range<_View> && + is_reference_v<_InnerRange> && + forward_range<_InnerRange> && + common_range<_View> && + common_range<_InnerRange>) + return __iterator<__simple_view<_View>>{*this, ranges::end(__base_)}; + else + return __sentinel<__simple_view<_View>>{*this}; + } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() const + requires input_range && + is_reference_v> + { + using _ConstInnerRange = range_reference_t; + if constexpr (forward_range && + is_reference_v<_ConstInnerRange> && + forward_range<_ConstInnerRange> && + common_range && + common_range<_ConstInnerRange>) { + return __iterator{*this, ranges::end(__base_)}; + } else { + return __sentinel{*this}; + } + } + }; + + template + requires view<_View> && input_range> + template struct join_view<_View>::__sentinel { + template friend struct __sentinel; + + private: + using _Parent = __maybe_const<_Const, join_view>; + using _Base = __maybe_const<_Const, _View>; + sentinel_t<_Base> __end_ = sentinel_t<_Base>(); + + public: + _LIBCPP_HIDE_FROM_ABI + __sentinel() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __sentinel(_Parent& __parent) + : __end_(ranges::end(__parent.__base_)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __sentinel(__sentinel __s) + requires _Const && convertible_to, sentinel_t<_Base>> + : __end_(_VSTD::move(__s.__end_)) {} + + template + requires sentinel_for, iterator_t<__maybe_const<_OtherConst, _View>>> + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) { + return __x.__outer_ == __y.__end_; + } + }; + + template + requires view<_View> && input_range> + template struct join_view<_View>::__iterator + : public __join_view_iterator_category<__maybe_const<_Const, _View>> { + + template friend struct __iterator; + + private: + using _Parent = __maybe_const<_Const, join_view>; + using _Base = __maybe_const<_Const, _View>; + using _Outer = iterator_t<_Base>; + using _Inner = iterator_t>; + + static constexpr bool __ref_is_glvalue = is_reference_v>; + + public: + _Outer __outer_ = _Outer(); + + private: + optional<_Inner> __inner_; + _Parent *__parent_ = nullptr; + + _LIBCPP_HIDE_FROM_ABI + constexpr void __satisfy() { + for (; __outer_ != ranges::end(__parent_->__base_); ++__outer_) { + auto&& __inner = [&]() -> auto&& { + if constexpr (__ref_is_glvalue) + return *__outer_; + else + return __parent_->__cache_.__emplace_deref(__outer_); + }(); + __inner_ = ranges::begin(__inner); + if (*__inner_ != ranges::end(__inner)) + return; + } + + if constexpr (__ref_is_glvalue) + __inner_.reset(); + } + + public: + using iterator_concept = _If< + __ref_is_glvalue && bidirectional_range<_Base> && bidirectional_range>, + bidirectional_iterator_tag, + _If< + __ref_is_glvalue && forward_range<_Base> && forward_range>, + forward_iterator_tag, + input_iterator_tag + > + >; + + using value_type = range_value_t>; + + using difference_type = common_type_t< + range_difference_t<_Base>, range_difference_t>>; + + _LIBCPP_HIDE_FROM_ABI + __iterator() requires default_initializable<_Outer> = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator(_Parent& __parent, _Outer __outer) + : __outer_(_VSTD::move(__outer)) + , __parent_(_VSTD::addressof(__parent)) { + __satisfy(); + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator(__iterator __i) + requires _Const && + convertible_to, _Outer> && + convertible_to, _Inner> + : __outer_(_VSTD::move(__i.__outer_)) + , __inner_(_VSTD::move(__i.__inner_)) + , __parent_(__i.__parent_) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator*() const { + return **__inner_; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr _Inner operator->() const + requires __has_arrow<_Inner> && copyable<_Inner> + { + return *__inner_; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator++() { + auto&& __inner = [&]() -> auto&& { + if constexpr (__ref_is_glvalue) + return *__outer_; + else + return *__parent_->__cache_; + }(); + if (++*__inner_ == ranges::end(__inner)) { + ++__outer_; + __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_range<_Base> && + forward_range> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator& operator--() + requires __ref_is_glvalue && + bidirectional_range<_Base> && + bidirectional_range> && + common_range> + { + if (__outer_ == ranges::end(__parent_->__base_)) + __inner_ = ranges::end(*--__outer_); + + // Skip empty inner ranges when going backwards. + while (*__inner_ == ranges::begin(*__outer_)) { + __inner_ = ranges::end(*--__outer_); + } + + --*__inner_; + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr __iterator operator--(int) + requires __ref_is_glvalue && + bidirectional_range<_Base> && + bidirectional_range> && + common_range> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __iterator& __x, const __iterator& __y) + requires __ref_is_glvalue && + equality_comparable> && + equality_comparable>> + { + return __x.__outer_ == __y.__outer_ && __x.__inner_ == __y.__inner_; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr decltype(auto) iter_move(const __iterator& __i) + noexcept(noexcept(ranges::iter_move(*__i.__inner_))) + { + return ranges::iter_move(*__i.__inner_); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr void iter_swap(const __iterator& __x, const __iterator& __y) + noexcept(noexcept(ranges::iter_swap(*__x.__inner_, *__y.__inner_))) + requires indirectly_swappable<_Inner> + { + return ranges::iter_swap(*__x.__inner_, *__y.__inner_); + } + }; + + template + explicit join_view(_Range&&) -> join_view>; + +} // namespace ranges + +#undef _CONSTEXPR_TERNARY + +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_JOIN_VIEW_H diff --git a/libcxx/include/__ranges/non_propagating_cache.h b/libcxx/include/__ranges/non_propagating_cache.h --- a/libcxx/include/__ranges/non_propagating_cache.h +++ b/libcxx/include/__ranges/non_propagating_cache.h @@ -85,6 +85,14 @@ constexpr void __set(_Tp const& __value) { __value_.emplace(__value); } _LIBCPP_HIDE_FROM_ABI constexpr void __set(_Tp&& __value) { __value_.emplace(_VSTD::move(__value)); } + + template + _LIBCPP_HIDE_FROM_ABI + constexpr _Tp& __emplace_deref(const _Other& __value) { + __value_.reset(); + __value_.emplace(*__value); + return *__value_; + } }; struct __empty_cache { }; diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -659,6 +659,7 @@ module enable_borrowed_range { private header "__ranges/enable_borrowed_range.h" } module enable_view { private header "__ranges/enable_view.h" } module iota_view { private header "__ranges/iota_view.h" } + module join_view { private header "__ranges/join_view.h" } module non_propagating_cache { private header "__ranges/non_propagating_cache.h" } module ref_view { private header "__ranges/ref_view.h" } module reverse_view { private header "__ranges/reverse_view.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -184,6 +184,11 @@ template inline constexpr bool enable_borrowed_range> = true; + + // [range.join], join view + template + requires view && input_range> + class join_view; } */ @@ -207,6 +212,7 @@ #include <__ranges/enable_borrowed_range.h> #include <__ranges/enable_view.h> #include <__ranges/iota_view.h> +#include <__ranges/join_view.h> #include <__ranges/ref_view.h> #include <__ranges/reverse_view.h> #include <__ranges/take_view.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/join_view.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/join_view.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/join_view.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__ranges/join_view.h'}} +#include <__ranges/join_view.h> diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/base.pass.cpp @@ -0,0 +1,62 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr V base() const& requires copy_constructible; +// constexpr V base() &&; + +#include +#include + +#include "test_macros.h" +#include "types.h" + +constexpr bool hasLValueQualifiedBase(auto&& view) { + return requires { view.base(); }; +} + +constexpr bool test() { + int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; + + { + ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; + auto jv = std::ranges::join_view(ParentView{children}); + assert(std::move(jv).base().ptr_ == children); + + static_assert(!hasLValueQualifiedBase(jv)); + ASSERT_SAME_TYPE(decltype(std::move(jv).base()), ParentView); + } + + { + std::ranges::join_view jv(buffer); + assert(jv.base().base() == buffer + 0); + + static_assert(hasLValueQualifiedBase(jv)); + ASSERT_SAME_TYPE(decltype(jv.base()), std::ranges::ref_view); + } + + { + const std::ranges::join_view jv(buffer); + assert(jv.base().base() == buffer + 0); + + static_assert(hasLValueQualifiedBase(jv)); + ASSERT_SAME_TYPE(decltype(jv.base()), std::ranges::ref_view); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/begin.pass.cpp @@ -0,0 +1,97 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto begin(); +// constexpr auto begin() const; + +#include +#include + +#include "test_macros.h" +#include "types.h" + +constexpr bool test() { + int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; + + { + ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; + auto jv = std::ranges::join_view(ParentView{children}); + assert(*jv.begin() == 1111); + } + + { + CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 1), CopyableChild(buffer[3], 0)}; + auto jv = std::ranges::join_view(ParentView{children}); + assert(*jv.begin() == 1111); + } + // Parent is empty. + { + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + std::ranges::join_view jv(ParentView(children, 0)); + assert(jv.begin() == jv.end()); + } + // Parent size is one. + { + CopyableChild children[1] = {CopyableChild(buffer[0])}; + std::ranges::join_view jv(ParentView(children, 1)); + assert(*jv.begin() == 1111); + } + // Parent and child size is one. + { + CopyableChild children[1] = {CopyableChild(buffer[0], 1)}; + std::ranges::join_view jv(ParentView(children, 1)); + assert(*jv.begin() == 1111); + } + // Parent size is one child is empty + { + CopyableChild children[1] = {CopyableChild(buffer[0], 0)}; + std::ranges::join_view jv(ParentView(children, 1)); + assert(jv.begin() == jv.end()); + } + // Has all empty children. + { + CopyableChild children[4] = {CopyableChild(buffer[0], 0), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)}; + auto jv = std::ranges::join_view(ParentView{children}); + assert(jv.begin() == jv.end()); + } + // First child is empty, others are not. + { + CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)}; + auto jv = std::ranges::join_view(ParentView{children}); + assert(*jv.begin() == 1111); + } + // Last child is empty, others are not. + { + CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 4), CopyableChild(buffer[2], 4), CopyableChild(buffer[3], 0)}; + auto jv = std::ranges::join_view(ParentView{children}); + assert(*jv.begin() == 1111); + } + + { + std::ranges::join_view jv(buffer); + assert(*jv.begin() == 1111); + } + + { + const std::ranges::join_view jv(buffer); + assert(*jv.begin() == 1111); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.compile.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// explicit join_view(R&&) -> join_view>; + +#include + +#include "test_iterators.h" + +template +struct View : std::ranges::view_base { + // All friends here are defined to prevent GCC warnings. + friend T* begin(View&) { return nullptr; } + friend T* begin(View const&) { return nullptr; } + friend sentinel_wrapper end(View&) { return sentinel_wrapper(nullptr); } + friend sentinel_wrapper end(View const&) { return sentinel_wrapper(nullptr); } +}; + +template +struct Range { + friend T* begin(Range&) { return nullptr; } + friend T* begin(Range const&) { return nullptr; } + friend sentinel_wrapper end(Range&) { return sentinel_wrapper(nullptr); } + friend sentinel_wrapper end(Range const&) { return sentinel_wrapper(nullptr); } +}; + +template +struct BorrowedRange { + friend T* begin(BorrowedRange&) { return nullptr; } + friend T* begin(BorrowedRange const&) { return nullptr; } + friend sentinel_wrapper end(BorrowedRange&) { return sentinel_wrapper(nullptr); } + friend sentinel_wrapper end(BorrowedRange const&) { return sentinel_wrapper(nullptr); } +}; + +template<> +inline constexpr bool std::ranges::enable_borrowed_range>> = true; + +void testCTAD() { + View> v; + Range> r; + BorrowedRange> br; + + static_assert(std::same_as< + decltype(std::ranges::join_view(v)), + std::ranges::join_view>> + >); + static_assert(std::same_as< + decltype(std::ranges::join_view(r)), + std::ranges::join_view>>> + >); + // std::ranges::join_view(std::move(r)) invalid. RValue range must be borrowed. + static_assert(std::same_as< + decltype(std::ranges::join_view(br)), + std::ranges::join_view>>> + >); + static_assert(std::same_as< + decltype(std::ranges::join_view(std::move(br))), + std::ranges::join_view *, + sentinel_wrapper *>, + std::ranges::subrange_kind::unsized>> + >); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.verify.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctad.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// explicit join_view(R&&) -> join_view>; + +// Tests that the deduction guide is explicit. + +#include + +#include "test_iterators.h" + +template +struct Range { + friend T* begin(Range&) { return nullptr; } + friend T* begin(Range const&) { return nullptr; } + friend sentinel_wrapper end(Range&) { return sentinel_wrapper(nullptr); } + friend sentinel_wrapper end(Range const&) { return sentinel_wrapper(nullptr); } +}; + +void testExplicitCTAD() { + Range> r; + std::ranges::join_view v = r; // expected-error {{no viable constructor or deduction guide for deduction of template arguments of 'join_view'}} +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.base.pass.cpp @@ -0,0 +1,49 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr explicit join_view(V base); + +#include +#include + +#include "test_macros.h" +#include "types.h" + +constexpr bool test() { + int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; + + { + ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; + auto jv = std::ranges::join_view(ParentView{children}); + assert(std::move(jv).base().ptr_ == children); + } + + { + std::ranges::join_view jv(buffer); + assert(jv.base().base() == buffer + 0); + } + + { + // Test explicitness. + static_assert( std::is_constructible_v>, ParentView>); + static_assert(!std::is_convertible_v>, ParentView>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/ctor.default.pass.cpp @@ -0,0 +1,37 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// join_view() requires default_initializable = default; + +#include +#include + +#include "test_macros.h" +#include "types.h" + + +constexpr bool test() { + std::ranges::join_view> jv; + assert(std::move(jv).base().ptr_ == globalChildren); + + static_assert( std::default_initializable>>); + static_assert(!std::default_initializable>); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/end.pass.cpp @@ -0,0 +1,120 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr auto end(); +// constexpr auto end() const; + +#include +#include +#include + +#include "test_macros.h" +#include "types.h" + + +constexpr bool test() { + int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; + + // Non const common, forward range. + { + std::ranges::join_view jv(buffer); + assert(jv.end() == std::ranges::next(jv.begin(), 16)); + + static_assert(std::same_as); + } + + // Non const not common, input range. + { + ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; + auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.end() == std::ranges::next(jv.begin(), 16)); + + static_assert(!std::same_as); + } + + // Const common, forward range. + { + const std::ranges::join_view jv(buffer); + assert(jv.end() == std::ranges::next(jv.begin(), 16)); + + static_assert(std::same_as); + } + + // Const not common, input range. + { + static_assert(std::is_reference_v>); + + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + const auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.end() == std::ranges::next(jv.begin(), 16)); + + static_assert(!std::same_as); + } + + // Has some empty children. + { + CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 1), CopyableChild(buffer[3], 0)}; + auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.end() == std::ranges::next(jv.begin(), 5)); + } + // Parent is empty. + { + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + std::ranges::join_view jv(ParentView(children, 0)); + assert(jv.end() == jv.begin()); + } + // Parent size is one. + { + CopyableChild children[1] = {CopyableChild(buffer[0])}; + std::ranges::join_view jv(ParentView(children, 1)); + assert(jv.end() == std::ranges::next(jv.begin(), 4)); + } + // Parent and child size is one. + { + CopyableChild children[1] = {CopyableChild(buffer[0], 1)}; + std::ranges::join_view jv(ParentView(children, 1)); + assert(jv.end() == std::ranges::next(jv.begin())); + } + // Parent size is one child is empty + { + CopyableChild children[1] = {CopyableChild(buffer[0], 0)}; + std::ranges::join_view jv(ParentView(children, 1)); + assert(jv.end() == jv.begin()); + } + // Has all empty children. + { + CopyableChild children[4] = {CopyableChild(buffer[0], 0), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)}; + auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.end() == jv.begin()); + } + // First child is empty, others are not. + { + CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 0), CopyableChild(buffer[2], 0), CopyableChild(buffer[3], 0)}; + auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.end() == std::ranges::next(jv.begin(), 4)); + } + // Last child is empty, others are not. + { + CopyableChild children[4] = {CopyableChild(buffer[0], 4), CopyableChild(buffer[1], 4), CopyableChild(buffer[2], 4), CopyableChild(buffer[3], 0)}; + auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.end() == std::ranges::next(jv.begin(), 12)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/general.pass.cpp @@ -0,0 +1,51 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// General tests for join_view. This file does not test anything specifically. + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "types.h" + + +template +bool isEqual(R &r, I i) { + for (auto e : r) + if (e != *i++) + return false; + return true; +} + +int main(int, char**) { + { + int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; + int *flattened = reinterpret_cast(buffer); + + ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; + auto jv = std::ranges::join_view(ParentView(children)); + assert(isEqual(jv, flattened)); + } + + { + std::vector vec = {"Hello", ",", " ", "World", "!"}; + std::string check = "Hello, World!"; + std::ranges::join_view jv(vec); + assert(isEqual(jv, check.begin())); + } + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/arrow.pass.cpp @@ -0,0 +1,50 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr InnerIter operator->() const +// requires has-arrow && copyable; + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + Box buffer[4][4] = {{{1111}, {2222}, {3333}, {4444}}, {{555}, {666}, {777}, {888}}, {{99}, {1010}, {1111}, {1212}}, {{13}, {14}, {15}, {16}}}; + + { + // Copyable input iterator with arrow. + ValueView children[4] = {ValueView(buffer[0]), ValueView(buffer[1]), ValueView(buffer[2]), ValueView(buffer[3])}; + std::ranges::join_view jv(ValueView>{children}); + assert(jv.begin()->x == 1111); + } + + { + std::ranges::join_view jv(buffer); + assert(jv.begin()->x == 1111); + } + + { + const std::ranges::join_view jv(buffer); + assert(jv.begin()->x == 1111); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.default.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// iterator() requires default_initializable = default; + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +template +struct DefaultCtorParent : std::ranges::view_base { + T *ptr_; + constexpr DefaultCtorParent(T *ptr) : ptr_(ptr) {} + + constexpr cpp17_input_iterator begin() { return cpp17_input_iterator(ptr_); } + constexpr cpp17_input_iterator begin() const { return cpp17_input_iterator(ptr_); } + constexpr T *end() { return ptr_ + 4; } + constexpr const T *end() const { return ptr_ + 4; } +}; + +template +constexpr bool operator==(const cpp17_input_iterator &lhs, const T *rhs) { return lhs.base() == rhs; } +template +constexpr bool operator==(const T *lhs, const cpp17_input_iterator &rhs) { return rhs.base() == lhs; } + +constexpr bool test() { + using Base = DefaultCtorParent; + // Note, only the outer iterator is default_initializable: + static_assert( std::default_initializable>); + static_assert(!std::default_initializable>>); + + std::ranges::iterator_t> iter1; + (void) iter1; + + static_assert(!std::default_initializable>>>); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.other.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator(iterator i); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + std::ranges::join_view jv(CopyableParent{children}); + auto iter1 = jv.begin(); + std::ranges::iterator_t iter2 = iter1; + assert(iter1 == iter2); + + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/ctor.parent.outer.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator(Parent& parent, OuterIter outer); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + CopyableParent parent{children}; + std::ranges::join_view jv(parent); + std::ranges::iterator_t iter(jv, std::ranges::begin(parent)); + assert(*iter == 1); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/decrement.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator& operator--(); +// constexpr iterator operator--(int); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + { + // outer == ranges::end + std::ranges::join_view jv(buffer); + auto iter = std::next(jv.begin(), 16); + for (int i = 16; i != 0; --i) { + assert(*--iter == i); + } + } + { + // outer == ranges::end + std::ranges::join_view jv(buffer); + auto iter = std::next(jv.begin(), 13); + for (int i = 13; i != 0; --i) { + assert(*--iter == i); + } + } + { + // outer != ranges::end + std::ranges::join_view jv(buffer); + auto iter = std::next(jv.begin(), 12); + for (int i = 12; i != 0; --i) { + assert(*--iter == i); + } + } + { + // outer != ranges::end + std::ranges::join_view jv(buffer); + auto iter = std::next(jv.begin()); + for (int i = 1; i != 0; --i) { + assert(*--iter == i); + } + } + { + int small[2][1] = {{1}, {2}}; + std::ranges::join_view jv(small); + auto iter = std::next(jv.begin(), 2); + for (int i = 2; i != 0; --i) { + assert(*--iter == i); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/eq.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr bool operator==(const iterator& x, const iterator& y); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + std::ranges::join_view jv(buffer); + auto iter1 = jv.begin(); + auto iter2 = jv.begin(); + assert(iter1 == iter2); + iter1++; + assert(iter1 != iter2); + iter2++; + assert(iter1 == iter2); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/increment.pass.cpp @@ -0,0 +1,160 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr iterator& operator++(); +// constexpr void operator++(int); +// constexpr iterator operator++(int); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + // This way if we read past end we'll catch the error. + int buffer1[2][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}}; + int dummy = 42; + (void) dummy; + int buffer2[2][4] = {{9, 10, 11, 12}, {13, 14, 15, 16}}; + + // operator++(int); + { + std::ranges::join_view jv(buffer1); + auto iter = jv.begin(); + for (int i = 1; i < 9; ++i) { + assert(*iter++ == i); + } + } + { + ValueView children[4] = {ValueView(buffer1[0]), ValueView(buffer1[1]), ValueView(buffer2[0]), ValueView(buffer2[1])}; + std::ranges::join_view jv(ValueView>{children}); + auto iter = jv.begin(); + for (int i = 1; i < 17; ++i) { + assert(*iter == i); + iter++; + } + + ASSERT_SAME_TYPE(decltype(iter++), void); + } + { + std::ranges::join_view jv(buffer1); + auto iter = std::next(jv.begin(), 7); + assert(*iter++ == 8); + assert(iter == jv.end()); + } + { + int small[2][1] = {{1}, {2}}; + std::ranges::join_view jv(small); + auto iter = jv.begin(); + for (int i = 1; i < 3; ++i) { + assert(*iter++ == i); + } + } + // Has some empty children. + { + CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 1), CopyableChild(buffer2[1], 0)}; + auto jv = std::ranges::join_view(ParentView(children)); + auto iter = jv.begin(); + assert(*iter == 1); iter++; + assert(*iter == 2); iter++; + assert(*iter == 3); iter++; + assert(*iter == 4); iter++; + assert(*iter == 9); iter++; + assert(iter == jv.end()); + } + // Parent is empty. + { + CopyableChild children[4] = {CopyableChild(buffer1[0]), CopyableChild(buffer1[1]), CopyableChild(buffer2[0]), CopyableChild(buffer2[1])}; + std::ranges::join_view jv(ParentView(children, 0)); + assert(jv.begin() == jv.end()); + } + // Parent size is one. + { + CopyableChild children[1] = {CopyableChild(buffer1[0])}; + std::ranges::join_view jv(ParentView(children, 1)); + auto iter = jv.begin(); + assert(*iter == 1); iter++; + assert(*iter == 2); iter++; + assert(*iter == 3); iter++; + assert(*iter == 4); iter++; + assert(iter == jv.end()); + } + // Parent and child size is one. + { + CopyableChild children[1] = {CopyableChild(buffer1[0], 1)}; + std::ranges::join_view jv(ParentView(children, 1)); + auto iter = jv.begin(); + assert(*iter == 1); iter++; + assert(iter == jv.end()); + } + // Parent size is one child is empty + { + CopyableChild children[1] = {CopyableChild(buffer1[0], 0)}; + std::ranges::join_view jv(ParentView(children, 1)); + assert(jv.begin() == jv.end()); + } + // Has all empty children. + { + CopyableChild children[4] = {CopyableChild(buffer1[0], 0), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 0), CopyableChild(buffer2[1], 0)}; + auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.begin() == jv.end()); + } + // First child is empty, others are not. + { + CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 0), CopyableChild(buffer2[0], 0), CopyableChild(buffer2[1], 0)}; + auto jv = std::ranges::join_view(ParentView(children)); + auto iter = jv.begin(); + assert(*iter == 1); iter++; + assert(*iter == 2); iter++; + assert(*iter == 3); iter++; + assert(*iter == 4); iter++; + assert(iter == jv.end()); + } + // Last child is empty, others are not. + { + CopyableChild children[4] = {CopyableChild(buffer1[0], 4), CopyableChild(buffer1[1], 4), CopyableChild(buffer2[0], 4), CopyableChild(buffer2[1], 0)}; + auto jv = std::ranges::join_view(ParentView(children)); + auto iter = jv.begin(); + for (int i = 1; i < 13; ++i) { + assert(*iter == i); + iter++; + } + } + // operator++(); + { + std::ranges::join_view jv(buffer1); + auto iter = jv.begin(); + for (int i = 2; i < 9; ++i) { + assert(*++iter == i); + } + } + { + ValueView children[4] = {ValueView(buffer1[0]), ValueView(buffer1[1]), ValueView(buffer2[0]), ValueView(buffer2[1])}; + std::ranges::join_view jv(ValueView>{children}); + auto iter = jv.begin(); + for (int i = 2; i < 17; ++i) { + assert(*++iter == i); + } + + ASSERT_SAME_TYPE(decltype(++iter), decltype(iter)&); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.move.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr decltype(auto) iter_move(const iterator& i); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + std::ranges::join_view jv(buffer); + assert(std::ranges::iter_move(jv.begin()) == 1); + ASSERT_SAME_TYPE(decltype(std::ranges::iter_move(jv.begin())), int&&); + + static_assert(noexcept(std::ranges::iter_move(std::declval()))); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/iter.swap.pass.cpp @@ -0,0 +1,43 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr void iter_swap(const iterator& x, const iterator& y); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + std::ranges::join_view jv(buffer); + auto iter1 = jv.begin(); + auto iter2 = std::next(jv.begin()); + assert(*iter1 == 1); + assert(*iter2 == 2); + std::ranges::swap(iter1, iter2); + assert(*iter1 == 2); + assert(*iter2 == 1); + + static_assert(noexcept(std::ranges::iter_swap(iter1, iter2))); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/member_types.compile.pass.cpp @@ -0,0 +1,67 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// Iterator traits and member typedefs in join_view::. + +#include + +#include "test_iterators.h" +#include "test_macros.h" +#include "../types.h" + +template +struct ForwardView : std::ranges::view_base { + friend forward_iterator begin(ForwardView&) { return forward_iterator(nullptr); } + friend forward_iterator begin(ForwardView const&) { return forward_iterator(nullptr); } + friend forward_iterator end(ForwardView&) { return forward_iterator(nullptr); } + friend forward_iterator end(ForwardView const&) { return forward_iterator(nullptr); } +}; + +template +struct InputView : std::ranges::view_base { + friend cpp17_input_iterator begin(InputView&) { return cpp17_input_iterator(nullptr); } + friend cpp17_input_iterator begin(InputView const&) { return cpp17_input_iterator(nullptr); } + friend cpp17_input_iterator end(InputView&) { return cpp17_input_iterator(nullptr); } + friend cpp17_input_iterator end(InputView const&) { return cpp17_input_iterator(nullptr); } +}; + +template +concept HasIterCategory = requires { typename T::iterator_category; }; + +void test() { + { + int buffer[4][4]; + std::ranges::join_view jv(buffer); + using Iter = std::ranges::iterator_t; + + ASSERT_SAME_TYPE(Iter::iterator_concept, std::bidirectional_iterator_tag); + ASSERT_SAME_TYPE(Iter::iterator_category, std::bidirectional_iterator_tag); + ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(Iter::value_type, int); + } + { + using Iter = std::ranges::iterator_t>>>; + + ASSERT_SAME_TYPE(Iter::iterator_concept, std::forward_iterator_tag); + ASSERT_SAME_TYPE(Iter::iterator_category, std::forward_iterator_tag); + ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(Iter::value_type, int); + } + { + using Iter = std::ranges::iterator_t>>>; + + ASSERT_SAME_TYPE(Iter::iterator_concept, std::input_iterator_tag); + static_assert(!HasIterCategory); + ASSERT_SAME_TYPE(Iter::difference_type, std::ptrdiff_t); + ASSERT_SAME_TYPE(Iter::value_type, int); + } +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/star.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/star.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/iterator/star.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr decltype(auto) operator*() const; + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + { + std::ranges::join_view jv(buffer); + auto iter = jv.begin(); + for (int i = 1; i < 17; ++i) { + assert(*iter++ == i); + } + } + { + std::ranges::join_view jv(buffer); + auto iter = std::next(jv.begin(), 15); + assert(*iter++ == 16); + assert(iter == jv.end()); + } + { + ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; + auto jv = std::ranges::join_view(ParentView(children)); + auto iter = jv.begin(); + for (int i = 1; i < 17; ++i) { + assert(*iter == i); + ++iter; + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.default.pass.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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// sentinel() = default; + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + std::ranges::sentinel_t> sent; + (void) sent; + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.other.pass.cpp @@ -0,0 +1,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr sentinel(sentinel s); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + std::ranges::join_view jv(CopyableParent{children}); + auto sent1 = jv.end(); + std::ranges::sentinel_t sent2 = sent1; + (void) sent2; // We can't really do anything with these sentinels now :/ + + // We cannot create a non-const iterator from a const iterator. + static_assert(!std::constructible_from); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.parent.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/ctor.parent.pass.cpp @@ -0,0 +1,45 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr explicit sentinel(Parent& parent); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + CopyableParent parent{children}; + std::ranges::join_view jv(parent); + std::ranges::sentinel_t sent(jv); + assert(sent == std::ranges::next(jv.begin(), 16)); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + { + // Test explicitness. + using Parent = std::ranges::join_view>; + static_assert( std::is_constructible_v, Parent&>); + static_assert(!std::is_convertible_v, Parent&>); + } + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/sentinel/eq.pass.cpp @@ -0,0 +1,52 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// template +// requires sentinel_for, iterator_t>> +// friend constexpr bool operator==(const iterator& x, const sentinel& y); + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + int buffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; + + { + ChildView children[4] = {ChildView(buffer[0]), ChildView(buffer[1]), ChildView(buffer[2]), ChildView(buffer[3])}; + auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.end() == std::ranges::next(jv.begin(), 16)); + } + { + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + const auto jv = std::ranges::join_view(ParentView(children)); + assert(jv.end() == std::ranges::next(jv.begin(), 16)); + } + { + CopyableChild children[4] = {CopyableChild(buffer[0]), CopyableChild(buffer[1]), CopyableChild(buffer[2]), CopyableChild(buffer[3])}; + const std::ranges::join_view jvc(CopyableParent{children}); + std::ranges::join_view jv(CopyableParent{children}); + assert(jvc.end() == std::ranges::next(jv.begin(), 16)); + assert( jv.end() == std::ranges::next(jvc.begin(), 16)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h b/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.join.view/types.h @@ -0,0 +1,141 @@ +//===----------------------------------------------------------------------===// +// +// 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_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H + +#include + +#include "test_macros.h" +#include "test_iterators.h" + +int globalBuffer[4][4] = {{1111, 2222, 3333, 4444}, {555, 666, 777, 888}, {99, 1010, 1111, 1212}, {13, 14, 15, 16}}; + +struct ChildView : std::ranges::view_base { + int *ptr_; + + constexpr ChildView(int *ptr = globalBuffer[0]) : ptr_(ptr) {} + ChildView(const ChildView&) = delete; + ChildView(ChildView&&) = default; + ChildView& operator=(const ChildView&) = delete; + ChildView& operator=(ChildView&&) = default; + + constexpr cpp20_input_iterator begin() { return cpp20_input_iterator(ptr_); } + constexpr cpp20_input_iterator begin() const { return cpp20_input_iterator(ptr_); } + constexpr int *end() { return ptr_ + 4; } + constexpr const int *end() const { return ptr_ + 4; } +}; + +constexpr bool operator==(const cpp20_input_iterator &lhs, int* rhs) { return lhs.base() == rhs; } +constexpr bool operator==(int* lhs, const cpp20_input_iterator &rhs) { return rhs.base() == lhs; } + +ChildView globalChildren[4] = {ChildView(globalBuffer[0]), ChildView(globalBuffer[1]), ChildView(globalBuffer[2]), ChildView(globalBuffer[3])}; + +template +struct ParentView : std::ranges::view_base { + T *ptr_; + unsigned size_; + + constexpr ParentView(T *ptr, unsigned size = 4) + : ptr_(ptr), size_(size) {} + constexpr ParentView(ChildView *ptr = globalChildren, unsigned size = 4) + requires std::same_as + : ptr_(ptr), size_(size) {} + ParentView(const ParentView&) = delete; + ParentView(ParentView&&) = default; + ParentView& operator=(const ParentView&) = delete; + ParentView& operator=(ParentView&&) = default; + + constexpr cpp20_input_iterator begin() { return cpp20_input_iterator(ptr_); } + constexpr cpp20_input_iterator begin() const { return cpp20_input_iterator(ptr_); } + constexpr T *end() { return ptr_ + size_; } + constexpr const T *end() const { return ptr_ + size_; } +}; + +template +constexpr bool operator==(const cpp20_input_iterator &lhs, T *rhs) { return lhs.base() == rhs; } +template +constexpr bool operator==(T *lhs, const cpp20_input_iterator &rhs) { return rhs.base() == lhs; } + +struct CopyableChild : std::ranges::view_base { + int *ptr_; + unsigned size_; + constexpr CopyableChild(int *ptr = globalBuffer[0], unsigned size = 4) + : ptr_(ptr), size_(size) {} + + constexpr cpp17_input_iterator begin() { return cpp17_input_iterator(ptr_); } + constexpr cpp17_input_iterator begin() const { return cpp17_input_iterator(ptr_); } + constexpr int *end() { return ptr_ + size_; } + constexpr const int *end() const { return ptr_ + size_; } +}; + +constexpr bool operator==(const cpp17_input_iterator &lhs, const int* rhs) { return lhs.base() == rhs; } +constexpr bool operator==(const int* lhs, const cpp17_input_iterator &rhs) { return rhs.base() == lhs; } + +struct CopyableParent : std::ranges::view_base { + CopyableChild *ptr_; + constexpr CopyableParent(CopyableChild *ptr) : ptr_(ptr) {} + + constexpr cpp17_input_iterator begin() { return cpp17_input_iterator(ptr_); } + constexpr cpp17_input_iterator begin() const { return cpp17_input_iterator(ptr_); } + constexpr CopyableChild *end() { return ptr_ + 4; } + constexpr const CopyableChild *end() const { return ptr_ + 4; } +}; + +constexpr bool operator==(const cpp17_input_iterator &lhs, const CopyableChild *rhs) { return lhs.base() == rhs; } +constexpr bool operator==(const CopyableChild *lhs, const cpp17_input_iterator &rhs) { return rhs.base() == lhs; } + +struct Box { int x; }; + +template +struct InputValueIter { + typedef std::input_iterator_tag iterator_category; + typedef T value_type; + typedef int difference_type; + typedef T reference; + + T *ptr_; + constexpr InputValueIter(T *ptr) : ptr_(ptr) {} + + constexpr T operator*() const { return std::move(*ptr_); } + constexpr void operator++(int) { ++ptr_; } + constexpr InputValueIter& operator++() { ++ptr_; return *this; } + + constexpr T *operator->() { return ptr_; } +}; + +template +constexpr bool operator==(const InputValueIter &lhs, const T* rhs) { return lhs.ptr_ == rhs; } +template +constexpr bool operator==(const T* lhs, const InputValueIter &rhs) { return rhs.ptr_ == lhs; } + +template +struct ValueView : std::ranges::view_base { + InputValueIter ptr_; + + constexpr ValueView(T *ptr) : ptr_(ptr) {} + + constexpr ValueView(ValueView &&other) + : ptr_(other.ptr_) { other.ptr_.ptr_ = nullptr; } + + constexpr ValueView& operator=(ValueView &&other) { + ptr_ = other.ptr_; + other.ptr_ = InputValueIter(nullptr); + return *this; + } + + ValueView(const ValueView&) = delete; + ValueView& operator=(const ValueView&) = delete; + + constexpr InputValueIter begin() { return ptr_; } + constexpr const InputValueIter begin() const { return ptr_; } + constexpr T *end() { return ptr_.ptr_ + 4; } + constexpr const T *end() const { return ptr_.ptr_ + 4; } +}; + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_JOIN_TYPES_H diff --git a/libcxx/test/support/test_iterators.h b/libcxx/test/support/test_iterators.h --- a/libcxx/test/support/test_iterators.h +++ b/libcxx/test/support/test_iterators.h @@ -914,6 +914,13 @@ difference_type stride_displacement_ = 0; }; +template +concept sentinel_for_base = requires(U const& u) { + u.base(); + requires std::input_or_output_iterator>; + requires std::equality_comparable_with; +}; + template class sentinel_wrapper { public: @@ -927,6 +934,12 @@ constexpr const I& base() const& { return base_; } constexpr I base() && { return std::move(base_); } + template + requires sentinel_for_base + constexpr bool operator==(I2 const& other) const { + return base_ == other.base(); + } + private: I base_ = I(); }; diff --git a/libcxx/test/support/test_range.h b/libcxx/test/support/test_range.h --- a/libcxx/test/support/test_range.h +++ b/libcxx/test/support/test_range.h @@ -62,4 +62,10 @@ sentinel end() const; }; +template class I, class R> +constexpr auto make_archetype_range(R&& r) { + return std::ranges::subrange(I(std::ranges::begin(r)), sentinel_wrapper(std::ranges::end(r))); +} + + #endif // LIBCXX_TEST_SUPPORT_TEST_RANGE_H