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,In Progress `[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 @@ -197,6 +197,7 @@ __ranges/empty.h __ranges/enable_borrowed_range.h __ranges/enable_view.h + __ranges/join_view.h __ranges/non_propagating_cache.h __ranges/ref_view.h __ranges/single_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,352 @@ +// -*- 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/concepts.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/ref_view.h> +#include <__ranges/subrange.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_PUSH_MACROS +#include <__undef_macros> + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if !defined(_LIBCPP_HAS_NO_RANGES) + +#define _CONSTEXPR_TERNARY(cond, a, b) \ + [&]() -> auto&& { \ + if constexpr (cond) \ + return a; \ + else \ + return b; \ + }(); + +namespace ranges { + template + struct __inner_cache { + __non_propagating_cache> __inner_; + }; + + template + requires is_reference_v<_Tp> + struct __inner_cache<_Tp> { }; + + 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> + , public __inner_cache> { + private: + using _InnerRange = range_reference_t<_View>; + + template struct __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_; // TODO: lwg issue? + _Parent *__parent_ = nullptr; + + _LIBCPP_HIDE_FROM_ABI + constexpr void __satisfy() { + for (; __outer_ != ranges::end(__parent_->__base_); ++__outer_) { + auto&& __inner = _CONSTEXPR_TERNARY(__ref_is_glvalue, + *__outer_, + __parent_->__inner_.__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_initializable<_Inner> = 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 = _CONSTEXPR_TERNARY(__ref_is_glvalue, + *__outer_, + *__parent_->__inner_); + if (++*__inner_ == ranges::end(__inner)) { + ++__outer_; + __satisfy(); + } + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr void operator++(int) { + return ++*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_); + + 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 struct __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_; + } + }; + + _View __base_ = _View(); + + 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> + { + if constexpr (forward_range && + is_reference_v> && + forward_range> && + common_range && + common_range>) { + return __iterator{*this, ranges::end(__base_)}; + } else { + return __sentinel{*this}; + } + } + }; + + template + explicit join_view(_Range&&) -> join_view>; + +} // namespace ranges + +#undef _CONSTEXPR_TERNARY + +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +_LIBCPP_POP_MACROS + +#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 @@ -629,6 +629,7 @@ module empty_view { private header "__ranges/empty_view.h" } module enable_borrowed_range { private header "__ranges/enable_borrowed_range.h" } module enable_view { private header "__ranges/enable_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 size { private header "__ranges/size.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -166,6 +166,11 @@ template requires is_object_v class single_view; + + // [range.join], join view + template + requires view && input_range> + class join_view; } */ @@ -187,6 +192,7 @@ #include <__ranges/empty_view.h> #include <__ranges/enable_borrowed_range.h> #include <__ranges/enable_view.h> +#include <__ranges/join_view.h> #include <__ranges/ref_view.h> #include <__ranges/single_view.h> #include <__ranges/size.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,63 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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])}; + std::ranges::join_view jv(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,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: gcc-10 +// 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])}; + std::ranges::join_view jv(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: gcc-10 +// 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 { + friend T* begin(View&); + friend T* begin(View const&); + friend sentinel_wrapper end(View&); + friend sentinel_wrapper end(View const&); +}; + +template +struct Range { + friend T* begin(Range&); + friend T* begin(Range const&); + friend sentinel_wrapper end(Range&); + friend sentinel_wrapper end(Range const&); +}; + +template +struct BorrowedRange { + friend T* begin(BorrowedRange&); + friend T* begin(BorrowedRange const&); + friend sentinel_wrapper end(BorrowedRange&); + friend sentinel_wrapper end(BorrowedRange const&); +}; + +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/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,44 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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])}; + std::ranges::join_view jv(ParentView{children}); + assert(std::move(jv).base().ptr_ == children); + } + + { + std::ranges::join_view jv(buffer); + assert(jv.base().base() == buffer + 0); + } + + 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,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: gcc-10 +// 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,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: gcc-10 +// 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])}; + std::ranges::join_view jv(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 std::ranges::join_view jv(CopyableParent{children}); + assert(jv.end() == std::ranges::next(jv.begin(), 16)); + + static_assert(!std::same_as); + } + + 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,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: gcc-10 +// 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])}; + std::ranges::join_view jv(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,91 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr InnerIter operator->() const +// requires has-arrow && copyable; + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +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) {} + ValueView(const ValueView&) = delete; + ValueView(ValueView&&) = default; + ValueView& operator=(const ValueView&) = delete; + ValueView& operator=(ValueView&&) = default; + + 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; } +}; + +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,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: gcc-10 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// iterator() requires default_initializable && +// default_initializable = default; + +#include +#include + +#include "test_macros.h" +#include "../types.h" + +constexpr bool test() { + std::ranges::iterator_t> iter1; + std::ranges::iterator_t> iter2; + assert(iter1 == iter2); + + 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,42 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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,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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: gcc-10 +// 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,75 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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,42 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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,57 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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() { + 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()); + } + { + 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); + } + } + + 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,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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: gcc-10 +// 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,44 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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,68 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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&); + friend forward_iterator begin(ForwardView const&); + friend forward_iterator end(ForwardView&); + friend forward_iterator end(ForwardView const&); +}; + +template +struct InputView : std::ranges::view_base { + friend cpp17_input_iterator begin(InputView&); + friend cpp17_input_iterator begin(InputView const&); + friend cpp17_input_iterator end(InputView&); + friend cpp17_input_iterator end(InputView const&); +}; + +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,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: gcc-10 +// 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])}; + std::ranges::join_view jv(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,34 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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,42 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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,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 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: gcc-10 +// 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()); + + 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,53 @@ +//===----------------------------------------------------------------------===// +// +// 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: gcc-10 +// 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])}; + std::ranges::join_view jv(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 jv(CopyableParent{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,89 @@ +//===----------------------------------------------------------------------===// +// +// 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_; + + constexpr ParentView(T *ptr) : ptr_(ptr) {} + constexpr ParentView(ChildView *ptr = globalChildren) + requires std::same_as + : ptr_(ptr) {} + 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_ + 4; } + constexpr const T *end() const { return ptr_ + 4; } +}; + +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_; + constexpr CopyableChild(int *ptr = globalBuffer[0]) : ptr_(ptr) {} + + 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_ + 4; } + constexpr const int *end() const { return ptr_ + 4; } +}; + +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; } + +#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