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 @@ -155,7 +155,7 @@ `[range.transform] <https://wg21.link/range.transform>`_,`transform_view <https://llvm.org/D103056>`_,[range.all],Zoe Carver,✅ `[range.take] <https://wg21.link/range.take>`_,`take_view <https://llvm.org/D106507>`_,[range.all],Zoe Carver,✅ `[range.join] <https://wg21.link/range.join>`_,`join_view <https://llvm.org/D107671>`_,[range.all],Zoe Carver,✅ -`[range.split] <https://wg21.link/range.split>`_,`split_view (renamed to lazy_split_view by P2210) <https://llvm.org/D107500>`_,[range.all],Zoe Carver,In progress +`[range.split] <https://wg21.link/range.split>`_,`split_view (renamed to lazy_split_view by P2210) <https://llvm.org/D107500>`_,[range.all],Zoe Carver and Konstantin Varlamov,✅ `[range.counted] <https://wg21.link/range.counted>`_,`view::counted <https://llvm.org/D106923>`_,[range.subrange],Zoe Carver,✅ `[range.common] <https://wg21.link/range.common>`_,`common_view <https://llvm.org/D105753>`_,[range.all],Zoe Carver,✅ `[range.reverse] <https://wg21.link/range.reverse>`_,`reverse_view <https://llvm.org/D107096>`_,[range.all],Zoe Carver,✅ diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -345,6 +345,7 @@ __ranges/enable_view.h __ranges/iota_view.h __ranges/join_view.h + __ranges/lazy_split_view.h __ranges/non_propagating_cache.h __ranges/owning_view.h __ranges/range_adaptor.h diff --git a/libcxx/include/__ranges/lazy_split_view.h b/libcxx/include/__ranges/lazy_split_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/lazy_split_view.h @@ -0,0 +1,381 @@ +// -*- 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_LAZY_SPLIT_VIEW_H +#define _LIBCPP___RANGES_LAZY_SPLIT_VIEW_H + +#include <__algorithm/find.h> +#include <__algorithm/mismatch.h> +#include <__concepts/constructible.h> +#include <__config> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/default_sentinel.h> +#include <__iterator/incrementable_traits.h> +#include <__iterator/indirectly_comparable.h> +#include <__iterator/iterator_traits.h> +#include <__iterator/iter_swap.h> +#include <__memory/addressof.h> +#include <__ranges/access.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__ranges/non_propagating_cache.h> +#include <__ranges/range_adaptor.h> +#include <__ranges/single_view.h> +#include <__ranges/view_interface.h> +#include <__utility/move.h> +#include <type_traits> + +#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 <auto> struct __require_constant; + +template <class _Range> +concept __tiny_range = + sized_range<_Range> && + requires { typename __require_constant<remove_reference_t<_Range>::size()>; } && + (remove_reference_t<_Range>::size() <= 1); + +template <input_range _View, forward_range _Pattern> + requires view<_View> && view<_Pattern> && + indirectly_comparable<iterator_t<_View>, iterator_t<_Pattern>, ranges::equal_to> && + (forward_range<_View> || __tiny_range<_Pattern>) +class lazy_split_view : public view_interface<lazy_split_view<_View, _Pattern>> { + + _View __base_ = _View(); + _Pattern __pattern_ = _Pattern(); + + using _MaybeCurrent = _If<!forward_range<_View>, __non_propagating_cache<iterator_t<_View>>, __empty_cache>; + _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_; + + template <bool> struct __outer_iterator; + template <bool> struct __inner_iterator; + +public: + lazy_split_view() + requires default_initializable<_View> && default_initializable<_Pattern> = default; + + constexpr lazy_split_view(_View __base, _Pattern __pattern) + : __base_(std::move(__base)), __pattern_(std::move(__pattern)) {} + + template <input_range _Range> + requires constructible_from<_View, views::all_t<_Range>> && + constructible_from<_Pattern, single_view<range_value_t<_Range>>> + constexpr lazy_split_view(_Range&& __r, range_value_t<_Range> __e) + : __base_(views::all(std::forward<_Range>(__r))) + , __pattern_(ranges::single_view(std::move(__e))) {} + + constexpr _View base() const& requires copy_constructible<_View> { return __base_; } + constexpr _View base() && { return std::move(__base_); } + + constexpr auto begin() { + if constexpr (forward_range<_View>) { + return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::begin(__base_)}; + } else { + *__current_ = ranges::begin(__base_); + return __outer_iterator<false>{*this}; + } + } + + constexpr auto begin() const requires forward_range<_View> && forward_range<const _View> { + return __outer_iterator<true>{*this, ranges::begin(__base_)}; + } + + constexpr auto end() requires forward_range<_View> && common_range<_View> { + return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::end(__base_)}; + } + + constexpr auto end() const { + if constexpr (forward_range<_View> && forward_range<const _View> && common_range<const _View>) { + return __outer_iterator<true>{*this, ranges::end(__base_)}; + } else { + return default_sentinel; + } + } + +private: + + template <class> + struct __outer_iterator_category {}; + + template <forward_range _Tp> + struct __outer_iterator_category<_Tp> { + using iterator_category = input_iterator_tag; + }; + + template <bool _Const> struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> { + private: + template <bool> + friend class __inner_iterator; + + using _Parent = __maybe_const<_Const, lazy_split_view>; + using _Base = __maybe_const<_Const, _View>; + + _Parent* __parent_ = nullptr; + using _MaybeCurrent = _If<forward_range<_View>, iterator_t<_Base>, __empty_cache>; + _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent(); + bool __trailing_empty_ = false; + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto& __current() { + if constexpr (forward_range<_Base>) { + return __current_; + } else { + return *__parent_->__current_; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto& __current() const { + if constexpr (forward_range<_Base>) { + return __current_; + } else { + return *__parent_->__current_; + } + } + + public: + using iterator_concept = conditional_t<forward_range<_Base>, forward_iterator_tag, input_iterator_tag>; + using difference_type = range_difference_t<_Base>; + + struct value_type : view_interface<value_type> { + private: + __outer_iterator __i_ = __outer_iterator(); + + public: + value_type() = default; + constexpr explicit value_type(__outer_iterator __i) + : __i_(std::move(__i)) {} + + constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; } + constexpr default_sentinel_t end() const { return default_sentinel; } + }; + + __outer_iterator() = default; + + constexpr explicit __outer_iterator(_Parent& __parent) + requires (!forward_range<_Base>) + : __parent_(std::addressof(__parent)) {} + + constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current) + requires forward_range<_Base> + : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {} + + constexpr __outer_iterator(__outer_iterator<!_Const> __i) + requires _Const && convertible_to<iterator_t<_View>, iterator_t<_Base>> + : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {} + + constexpr value_type operator*() const { return value_type{*this}; } + + constexpr __outer_iterator& operator++() { + const auto __end = ranges::end(__parent_->__base_); + if (__current() == __end) { + __trailing_empty_ = false; + return *this; + } + + const auto [__pbegin, __pend] = ranges::subrange{__parent_->__pattern_}; + if (__pbegin == __pend) { + ++__current(); + + } else if constexpr (__tiny_range<_Pattern>) { + // TODO(varconst): should be `ranges::find`. + __current() = std::find(std::move(__current()), __end, *__pbegin); + if (__current() != __end) { + ++__current(); + if (__current() == __end) + __trailing_empty_ = true; + } + + } else { + do { + // TODO(varconst): should be `ranges::mismatch`. + const auto [__b, __p] = std::mismatch(__current(), __end, __pbegin, __pend); + if (__p == __pend) { + __current() = __b; + if (__current() == __end) { + __trailing_empty_ = true; + } + break; // The pattern matched; skip it. + } + } while (++__current() != __end); + } + + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Base>) { + auto __tmp = *this; + ++*this; + return __tmp; + } else { + ++*this; + } + } + + friend constexpr bool operator==(const __outer_iterator& __x, const __outer_iterator& __y) + requires forward_range<_Base> + { + return __x.__current_ == __y.__current_ && __x.__trailing_empty_ == __y.__trailing_empty_; + } + + friend constexpr bool operator==(const __outer_iterator& __x, default_sentinel_t) { + return __x.__current() == ranges::end(__x.__parent_->__base_) && !__x.__trailing_empty_; + } + }; + + template <class> + struct __inner_iterator_category {}; + + template <forward_range _Tp> + struct __inner_iterator_category<_Tp> { + using iterator_category = _If< + derived_from<typename iterator_traits<iterator_t<_Tp>>::iterator_category, forward_iterator_tag>, + forward_iterator_tag, + typename iterator_traits<iterator_t<_Tp>>::iterator_category + >; + }; + + template <bool _Const> struct __inner_iterator : private __inner_iterator_category<__maybe_const<_Const, _View>> { + private: + using _Base = __maybe_const<_Const, _View>; + __outer_iterator<_Const> __i_ = __outer_iterator<_Const>(); + bool __incremented_ = false; + + public: + using iterator_concept = typename __outer_iterator<_Const>::iterator_concept; + using value_type = range_value_t<_Base>; + using difference_type = range_difference_t<_Base>; + + __inner_iterator() = default; + + constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) + : __i_(std::move(__i)) {} + + constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); } + constexpr iterator_t<_Base> base() && + requires forward_range<_View> { return std::move(__i_.__current()); } + + constexpr decltype(auto) operator*() const { return *__i_.__current(); } + + constexpr __inner_iterator& operator++() { + __incremented_ = true; + + if constexpr (!forward_range<_Base>) { + if constexpr (_Pattern::size() == 0) { + return *this; + } + } + + ++__i_.__current(); + return *this; + } + + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Base>) { + auto __tmp = *this; + ++*this; + return __tmp; + + } else { + ++*this; + } + } + + friend constexpr bool operator==(const __inner_iterator& __x, const __inner_iterator& __y) + requires forward_range<_Base> + { + return __x.__i_.__current() == __y.__i_.__current(); + } + + friend constexpr bool operator==(const __inner_iterator& __x, default_sentinel_t) { + auto [__pcur, __pend] = ranges::subrange{__x.__i_.__parent_->__pattern_}; + const auto __end = ranges::end(__x.__i_.__parent_->__base_); + + if constexpr (__tiny_range<_Pattern>) { + const auto& __cur = __x.__i_.__current(); + if (__cur == __end) return true; + if (__pcur == __pend) return __x.__incremented_; + + return *__cur == *__pcur; + + } else { + auto __cur = __x.__i_.__current(); + if (__cur == __end) return true; + if (__pcur == __pend) return __x.__incremented_; + + do { + if (*__cur != *__pcur) return false; + if (++__pcur == __pend) return true; + } while (++__cur != __end); + + return false; + } + } + + friend constexpr decltype(auto) iter_move(const __inner_iterator& __i) + noexcept(noexcept(ranges::iter_move(__i.__i_.__current()))) { + return ranges::iter_move(__i.__i_.__current()); + } + + friend constexpr void iter_swap(const __inner_iterator& __x, const __inner_iterator& __y) + noexcept(noexcept(ranges::iter_swap(__x.__i_.__current(), __y.__i_.__current()))) + requires indirectly_swappable<iterator_t<_Base>> { + ranges::iter_swap(__x.__i_.__current(), __y.__i_.__current()); + } + }; + +}; + +template <class _Range, class _Pattern> +lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view<views::all_t<_Range>, views::all_t<_Pattern>>; + +template <input_range _Range> +lazy_split_view(_Range&&, range_value_t<_Range>) + -> lazy_split_view<views::all_t<_Range>, single_view<range_value_t<_Range>>>; + +namespace views { +namespace __lazy_split_view { +struct __fn : __range_adaptor_closure<__fn> { + template <class _Range, class _Pattern> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Range&& __range, _Pattern&& __pattern) const + noexcept( noexcept(lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)))) + -> decltype( lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern))) + { return lazy_split_view(std::forward<_Range>(__range), std::forward<_Pattern>(__pattern)); } + + template <class _Pattern> + requires constructible_from<decay_t<_Pattern>, _Pattern> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Pattern&& __pattern) const + noexcept(is_nothrow_constructible_v<decay_t<_Pattern>, _Pattern>) { + return __range_adaptor_closure_t(std::__bind_back(*this, std::forward<_Pattern>(__pattern))); + } +}; +} // namespace __lazy_split_view + +inline namespace __cpo { + inline constexpr auto lazy_split = __lazy_split_view::__fn{}; +} // namespace __cpo +} // namespace views + +} // namespace ranges + +#endif // !defined(_LIBCPP_HAS_NO_RANGES) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___RANGES_LAZY_SPLIT_VIEW_H diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -813,6 +813,7 @@ 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 lazy_split_view { private header "__ranges/lazy_split_view.h" } module non_propagating_cache { private header "__ranges/non_propagating_cache.h" } module owning_view { private header "__ranges/owning_view.h" } module range_adaptor { private header "__ranges/range_adaptor.h" } diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -196,6 +196,20 @@ template<input_range V> requires view<V> && input_range<range_reference_t<V>> class join_view; + + // [range.lazy.split], lazy split view + template<class R> + concept tiny-range = see below; // exposition only + + template<input_range V, forward_range Pattern> + requires view<V> && view<Pattern> && + indirectly_comparable<iterator_t<V>, iterator_t<Pattern>, ranges::equal_to> && + (forward_range<V> || tiny-range<Pattern>) + class lazy_split_view; + + namespace views { + inline constexpr unspecified lazy_split = unspecified; + } } namespace std { @@ -245,6 +259,7 @@ #include <__ranges/enable_view.h> #include <__ranges/iota_view.h> #include <__ranges/join_view.h> +#include <__ranges/lazy_split_view.h> #include <__ranges/rbegin.h> #include <__ranges/ref_view.h> #include <__ranges/rend.h> diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/ranges/lazy_split_view.module.verify.cpp @@ -0,0 +1,15 @@ +//===----------------------------------------------------------------------===// +// +// 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/lazy_split_view.h'}} +#include <__ranges/lazy_split_view.h> diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/adaptor.pass.cpp @@ -0,0 +1,125 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::views::lazy_split + +#include <ranges> + +#include <array> +#include <cassert> +#include <concepts> +#include <string_view> +#include <utility> + +#include "test_iterators.h" +#include "types.h" + +template <class View, class T> +concept CanBePiped = requires (View&& view, T&& t) { + { std::forward<View>(view) | std::forward<T>(t) }; +}; + +static_assert(std::same_as<decltype(std::views::lazy_split), decltype(std::ranges::views::lazy_split)>); + +struct SomeView : std::ranges::view_base { + const std::string_view* v_; + constexpr SomeView(const std::string_view& v) : v_(&v) {} + constexpr auto begin() const { return v_->begin(); } + constexpr auto end() const { return v_->end(); } +}; + +constexpr bool test() { + using namespace std::string_view_literals; + + constexpr std::string_view input = "abc"; + constexpr std::string_view sep = "a"; + + // Test that std::views::lazy_split is a range adaptor. + + // Test `views::lazy_split(input, sep)`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view<SomeView, std::string_view>; + std::same_as<Result> auto result = std::views::lazy_split(view, sep); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `views::lazy_split(sep)(input)`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view<SomeView, std::string_view>; + std::same_as<Result> auto result = std::views::lazy_split(sep)(view); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `view | views::lazy_split`. + { + SomeView view(input); + + using Result = std::ranges::lazy_split_view<SomeView, std::string_view>; + std::same_as<Result> auto result = view | std::views::lazy_split(sep); + assert(result.base().begin() == input.begin()); + assert(result.base().end() == input.end()); + } + + // Test `adaptor | views::lazy_split`. + { + SomeView view(input); + auto f = [](char c) { return c; }; + auto partial = std::views::transform(f) | std::views::lazy_split(sep); + + using Result = std::ranges::lazy_split_view<std::ranges::transform_view<SomeView, decltype(f)>, std::string_view>; + std::same_as<Result> auto result = partial(view); + assert(result.base().base().begin() == input.begin()); + assert(result.base().base().end() == input.end()); + } + + // Test `views::lazy_split | adaptor`. + { + SomeView view(input); + auto f = [](auto v) { return v; }; + auto partial = std::views::lazy_split(sep) | std::views::transform(f); + + using Result = std::ranges::transform_view<std::ranges::lazy_split_view<SomeView, std::string_view>, decltype(f)>; + std::same_as<Result> auto result = partial(view); + assert(result.base().base().begin() == input.begin()); + assert(result.base().base().end() == input.end()); + } + + // Check SFINAE friendliness. + { + struct NotAView { }; + + static_assert(!std::is_invocable_v<decltype(std::views::lazy_split)>); + static_assert(!std::is_invocable_v<decltype(std::views::lazy_split), SomeView, NotAView>); + static_assert(!std::is_invocable_v<decltype(std::views::lazy_split), NotAView, SomeView>); + static_assert( std::is_invocable_v<decltype(std::views::lazy_split), SomeView, SomeView>); + + static_assert( CanBePiped<SomeView&, decltype(std::views::lazy_split)>); + static_assert( CanBePiped<char(&)[10], decltype(std::views::lazy_split)>); + static_assert(!CanBePiped<char(&&)[10], decltype(std::views::lazy_split)>); + static_assert(!CanBePiped<NotAView, decltype(std::views::lazy_split)>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/base.pass.cpp @@ -0,0 +1,60 @@ +//===----------------------------------------------------------------------===// +// +// 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 View base() const& requires copy_constructible<_View>; +// constexpr View base() &&; + +#include <ranges> + +#include <cassert> +#include <string_view> +#include <utility> +#include "types.h" + +template <class View> +concept CanCallLvalueBase = requires(View& v) { v.base(); }; +template <class View> +concept CanCallRvalueBase = requires(View& v) { std::move(v).base(); }; + +static_assert( CanCallLvalueBase<std::ranges::lazy_split_view<CopyableView, ForwardView>>); +static_assert(!CanCallLvalueBase<std::ranges::lazy_split_view<MoveOnlyView, ForwardView>>); +static_assert( CanCallRvalueBase<std::ranges::lazy_split_view<CopyableView, ForwardView>>); +static_assert( CanCallRvalueBase<std::ranges::lazy_split_view<MoveOnlyView, ForwardView>>); + +constexpr bool test() { + // Copyable input -- both lvalue and rvalue overloads of `base` are available. + { + constexpr std::string_view str = "abc def"; + std::ranges::lazy_split_view<std::string_view, std::string_view> v(str, " "); + + static_assert(std::same_as<decltype(v.base()), std::string_view>); + assert(v.base() == str); + + static_assert(std::same_as<decltype(std::move(v).base()), std::string_view>); + assert(std::move(v).base() == str); + } + + // Move-only input -- only the rvalue overload of `base` is available. + { + std::ranges::lazy_split_view<MoveOnlyView, ForwardView> v; + assert(std::move(v).base() == MoveOnlyView()); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/begin.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 begin(); +// constexpr auto begin() const requires forward_range<View> && forward_range<const View>; + +#include <ranges> + +#include <cassert> +#include <utility> +#include "test_iterators.h" +#include "types.h" + +template <class View, class Expected> +concept OuterIterConceptSameAs = + std::same_as<typename decltype(std::declval<View>().begin())::iterator_concept, Expected>; + +template <class View, class Expected> +concept InnerIterValueTypeSameAs = requires (View v) { + { *(*v.begin()).begin() } -> std::same_as<Expected>; +}; + +template <class View> +concept ConstBeginDisabled = !requires (const View v) { + { (*v.begin()) }; +}; + +constexpr bool test() { + // non-const: forward_range<View> && simple-view<View> -> outer-iterator<Const = true> + // const: forward_range<View> && forward_range<const View> -> outer-iterator<Const = true> + { + using V = ForwardView; + using P = V; + + static_assert(std::ranges::forward_range<V>); + static_assert(std::ranges::forward_range<const V>); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<V>); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<P>); + + std::ranges::lazy_split_view<V, P> v; + static_assert(OuterIterConceptSameAs<decltype(v), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(v), const char&>); + + const std::ranges::lazy_split_view<V, P> cv; + static_assert(OuterIterConceptSameAs<decltype(cv), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(cv), const char&>); + } + + // non-const: forward_range<View> && !simple-view<View> -> outer-iterator<Const = false> + // const: forward_range<View> && forward_range<const View> -> outer-iterator<Const = true> + { + using V = ForwardDiffView; + using P = V; + + static_assert(std::ranges::forward_range<V>); + static_assert(std::ranges::forward_range<const V>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<V>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<P>); + + std::ranges::lazy_split_view<V, P> v; + static_assert(OuterIterConceptSameAs<decltype(v), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(v), char&>); + + const std::ranges::lazy_split_view<V, P> cv; + static_assert(OuterIterConceptSameAs<decltype(cv), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(cv), const char&>); + } + + // non-const: forward_range<View> && !simple-view<View> -> outer-iterator<Const = false> + // const: forward_range<View> && !forward_range<const View> -> disabled + { + using V = ForwardOnlyIfNonConstView; + using P = V; + + static_assert(std::ranges::forward_range<V>); + static_assert(!std::ranges::forward_range<const V>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<V>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<P>); + + std::ranges::lazy_split_view<V, P> v; + static_assert(OuterIterConceptSameAs<decltype(v), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(v), const char&>); + + static_assert(ConstBeginDisabled<decltype(v)>); + } + + // non-const: !forward_range<View> && tiny-range<Pattern> -> outer-iterator<Const = false> + // const: !forward_range<View> -> disabled + { + using V = InputView; + using P = ForwardTinyView; + + static_assert(!std::ranges::forward_range<V>); + static_assert(std::ranges::forward_range<P>); + + std::ranges::lazy_split_view<V, P> v; + static_assert(OuterIterConceptSameAs<decltype(v), std::input_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(v), int&>); + + static_assert(ConstBeginDisabled<decltype(v)>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctad.compile.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: libcpp-has-no-incomplete-ranges + +// template <class R, class P> +// lazy_split_view(R&&, P&&) -> lazy_split_view<views::all_t<R>, views::all_t<P>>; +// +// template <input_range R> +// lazy_split_view(R&&, range_value_t<R>) -> lazy_split_view<views::all_t<R>, single_view<range_value_t<R>>>; + +#include <ranges> + +#include <concepts> +#include <type_traits> +#include <utility> +#include "types.h" + +template <class I1, class I2, class Expected> +constexpr void test() { + I1 i1{}; + I2 i2{}; + + std::ranges::lazy_split_view v(std::move(i1), std::move(i2)); + using O = decltype(std::move(v).base()); + static_assert(std::same_as<O, Expected>); +} + +constexpr void testCtad() { + // (Range, Pattern) + test<ForwardView, ForwardView, ForwardView>(); + test<ForwardRange, ForwardRange, std::ranges::views::all_t<ForwardRange>>(); + + // (Range, RangeElement) + test<ForwardRange, char, std::ranges::views::all_t<ForwardRange>>(); + test<InputRange, char, std::ranges::views::all_t<InputRange>>(); + + // (Range, RangeElement) with implicit conversion. + test<ForwardRange, bool, std::ranges::views::all_t<ForwardRange>>(); + test<InputRange, bool, std::ranges::views::all_t<InputRange>>(); + + // Note: CTAD from (InputRange, TinyForwardRange) doesn't work -- the deduction guide wraps the pattern in + // `views::all_t`, resulting in `views::owning_view<TinyForwardRange>`. That type would never satisfy `tiny-range` + // because `views::owning_view` contains a member function `size()` that shadows the static `size()` in + // `TinyForwardRange`. +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.default.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: libcpp-has-no-incomplete-ranges + +// lazy_split_view() requires default_initializable<V> && default_initializable<P> = default; + +#include <ranges> + +#include <cassert> +#include "test_macros.h" +#include "types.h" + +static_assert( std::is_default_constructible_v<std::ranges::lazy_split_view<ForwardView, ForwardView>>); +static_assert(!std::is_default_constructible_v<std::ranges::lazy_split_view<NoDefaultCtorForwardView, ForwardView>>); +static_assert(!std::is_default_constructible_v<std::ranges::lazy_split_view<ForwardView, NoDefaultCtorForwardView>>); + +LIBCPP_STATIC_ASSERT( std::is_nothrow_default_constructible_v<std::ranges::lazy_split_view<ForwardView, ForwardView>>); +static_assert(!std::is_nothrow_default_constructible_v<ThrowingDefaultCtorForwardView>); +static_assert(!std::is_nothrow_default_constructible_v< + std::ranges::lazy_split_view<ThrowingDefaultCtorForwardView, ForwardView>>); + +constexpr bool test() { + std::ranges::lazy_split_view<CopyableView, ForwardView> v; + assert(v.base() == CopyableView()); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.range.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 + +// template <input_range Range> +// requires constructible_from<View, views::all_t<Range>> && +// constructible_from<Pattern, single_view<range_value_t<Range>>> +// constexpr lazy_split_view(Range&& r, range_value_t<Range> e); + +#include <ranges> + +#include <cassert> +#include <string_view> +#include <utility> +#include "types.h" + +template <class V, class P, class R, class D> +concept CanConstruct = requires(R&& r, D&& d) { + std::ranges::lazy_split_view<V, P>(std::forward<R>(r), std::move(d)); +}; + +static_assert( CanConstruct<std::string_view, std::string_view, std::string_view, char>); +static_assert( CanConstruct<std::string_view, std::string_view, char(&)[5], char>); + +struct Empty {}; +static_assert(!CanConstruct<std::string_view, std::string_view, Empty, std::string_view>); +static_assert(!CanConstruct<std::string_view, std::string_view, std::string_view, Empty>); + +constexpr bool test() { + decltype(auto) str = "abc def"; + std::ranges::lazy_split_view<std::string_view, std::string_view> v(str, ' '); + assert(v.base() == std::string_view(str, sizeof(str))); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/ctor.view.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 + +// constexpr lazy_split_view(View base, Pattern pattern); + +#include <ranges> + +#include <cassert> +#include <string_view> + +constexpr bool test() { + constexpr std::string_view str = "abc def"; + std::ranges::lazy_split_view<std::string_view, std::string_view> v(str, " "); + assert(v.base() == str); + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/end.pass.cpp @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// 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() requires forward_range<View> && common_range<View>; +// constexpr auto end() const; + +#include <ranges> + +#include <cassert> +#include <utility> +#include "test_iterators.h" +#include "types.h" + +template <class View, class Expected> +concept OuterIterConceptSameAs = + std::same_as<typename decltype(std::declval<View>().end())::iterator_concept, Expected>; + +template <class View, class Expected> +concept InnerIterValueTypeSameAs = requires (View v) { + { *(*v.end()).begin() } -> std::same_as<Expected>; +}; + +struct ForwardViewCommonIfConst : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardViewCommonIfConst() = default; + constexpr ForwardViewCommonIfConst(const char* ptr) : view_(ptr) {} + constexpr ForwardViewCommonIfConst(std::string_view v) : view_(v) {} + constexpr ForwardViewCommonIfConst(ForwardViewCommonIfConst&&) = default; + constexpr ForwardViewCommonIfConst& operator=(ForwardViewCommonIfConst&&) = default; + constexpr ForwardViewCommonIfConst(const ForwardViewCommonIfConst&) = default; + constexpr ForwardViewCommonIfConst& operator=(const ForwardViewCommonIfConst&) = default; + constexpr forward_iterator<char*> begin() { return forward_iterator<char*>(nullptr); } + constexpr std::default_sentinel_t end() { return std::default_sentinel; } + constexpr forward_iterator<const char*> begin() const { return forward_iterator<const char*>(view_.begin()); } + constexpr forward_iterator<const char*> end() const { return forward_iterator<const char*>(view_.end()); } +}; +bool operator==(forward_iterator<char*>, std::default_sentinel_t) { return false; } + +struct ForwardViewNonCommonRange : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardViewNonCommonRange() = default; + constexpr ForwardViewNonCommonRange(const char* ptr) : view_(ptr) {} + constexpr ForwardViewNonCommonRange(std::string_view v) : view_(v) {} + constexpr ForwardViewNonCommonRange(ForwardViewNonCommonRange&&) = default; + constexpr ForwardViewNonCommonRange& operator=(ForwardViewNonCommonRange&&) = default; + constexpr ForwardViewNonCommonRange(const ForwardViewNonCommonRange&) = default; + constexpr ForwardViewNonCommonRange& operator=(const ForwardViewNonCommonRange&) = default; + constexpr forward_iterator<char*> begin() { return forward_iterator<char*>(nullptr); } + constexpr std::default_sentinel_t end() { return std::default_sentinel; } + constexpr forward_iterator<const char*> begin() const { return forward_iterator<const char*>(view_.begin()); } + constexpr std::default_sentinel_t end() const { return std::default_sentinel; } +}; +bool operator==(forward_iterator<const char*>, std::default_sentinel_t) { return false; } + +constexpr bool test() { + // non-const: forward_range<V> && simple_view<V> && simple_view<P> -> outer-iterator<Const = true> + // const: forward_range<V> && common_range<V> -> outer-iterator<Const = true> + { + using V = ForwardView; + using P = V; + + static_assert(std::ranges::forward_range<V>); + static_assert(std::ranges::common_range<const V>); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<V>); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<P>); + + std::ranges::lazy_split_view<V, P> v; + static_assert(OuterIterConceptSameAs<decltype(v), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(v), const char&>); + + const std::ranges::lazy_split_view<V, P> cv; + static_assert(OuterIterConceptSameAs<decltype(cv), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(cv), const char&>); + } + + // non-const: forward_range<V> && common_range<V> && simple_view<V> && !simple_view<P> -> outer-iterator<Const=false> + // const: forward_range<V> && forward_range<const V> && common_range<const V> -> outer-iterator<Const = false> + { + using V = ForwardView; + using P = ForwardNonSimpleView; + + static_assert(std::ranges::forward_range<V>); + static_assert(std::ranges::common_range<V>); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<V>); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<P>); + static_assert(std::ranges::forward_range<const V>); + static_assert(std::ranges::common_range<const V>); + + std::ranges::lazy_split_view<V, P> v; + static_assert(OuterIterConceptSameAs<decltype(v), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(v), const char&>); + + const std::ranges::lazy_split_view<V, P> cv; + static_assert(OuterIterConceptSameAs<decltype(cv), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(cv), const char&>); + } + + // non-const: forward_range<V> && !common_range<V> -> disabled + // const: forward_range<V> && forward_range<const V> && common_range<const V> -> outer-iterator<Const = true> + { + using V = ForwardViewCommonIfConst; + using P = V; + + static_assert(std::ranges::forward_range<V>); + static_assert(!std::ranges::common_range<V>); + static_assert(std::ranges::forward_range<const V>); + static_assert(std::ranges::common_range<const V>); + + std::ranges::lazy_split_view<V, P> v; + static_assert(OuterIterConceptSameAs<decltype(v), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(v), const char&>); + + const std::ranges::lazy_split_view<V, P> cv; + static_assert(OuterIterConceptSameAs<decltype(cv), std::forward_iterator_tag>); + static_assert(InnerIterValueTypeSameAs<decltype(cv), const char&>); + } + + // non-const: forward_range<V> && !common_range<V> -> disabled + // const: forward_range<V> && forward_range<const V> && !common_range<const V> -> outer-iterator<Const = false> + { + using V = ForwardViewNonCommonRange; + using P = V; + + static_assert(std::ranges::forward_range<V>); + static_assert(!std::ranges::common_range<V>); + static_assert(std::ranges::forward_range<const V>); + static_assert(!std::ranges::common_range<const V>); + + std::ranges::lazy_split_view<V, P> v; + static_assert(std::same_as<decltype(v.end()), std::default_sentinel_t>); + + const std::ranges::lazy_split_view<V, P> cv; + static_assert(std::same_as<decltype(cv.end()), std::default_sentinel_t>); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/general.pass.cpp @@ -0,0 +1,399 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// class std::ranges::lazy_split_view; + +#include <ranges> + +#include <algorithm> +#include <array> +#include <cassert> +#include <string_view> +#include <utility> + +template <class L, class R> +constexpr bool eq(const L& left, const R& right) { + auto l = left.begin(); + auto r = right.begin(); + + for (; l != left.end(); ++l, ++r) { + if (r == right.end()) { + auto next = l; + ++next; + return *l == '\0' && next == left.end(); + } + if (*l != *r) return false; + } + + return r == right.end(); +} + +template <class T, size_t N, class Separator, class U, size_t M> +constexpr bool test(const T (&input)[N], Separator separator, std::array<U, M> expected) { + std::ranges::lazy_split_view v(input, separator); + + return std::equal(v.begin(), v.end(), expected.begin(), expected.end(), + [](const auto& split_value, const auto& expected_value) { + // TODO(varconst): use `std::ranges::equal`. + return eq(split_value, expected_value); + }); +} + +template <class T, size_t N, class Separator, class U, size_t M> +constexpr bool test_with_piping(const T (&input)[N], Separator separator, std::array<U, M> expected) { + auto expected_it = expected.begin(); + for (auto e : input | std::ranges::views::lazy_split(separator)) { + // TODO(varconst): use `std::ranges::equal`. + if (expected_it == expected.end()) return false; + + if (!eq(e, *expected_it)) return false; + ++expected_it; + } + + return expected_it == expected.end(); +} + +constexpr bool test_l_r_values() { + using namespace std::string_view_literals; + + // Both lvalues and rvalues can be used as input. + { + // Lvalues. + { + std::string_view input = "abc"; + std::string_view sep = " "; + std::ranges::lazy_split_view v(input, sep); + (void)v; + } + + // Const lvalues. + { + const std::string_view input = "abc"; + const std::string_view sep = " "; + std::ranges::lazy_split_view v(input, sep); + (void)v; + } + + // Rvalues. + { + std::ranges::lazy_split_view v("abc"sv, " "sv); + (void)v; + } + + // Const rvalues. + { + const std::string_view input = "abc"; + const std::string_view sep = " "; + std::ranges::lazy_split_view v(std::move(input), std::move(sep)); + (void)v; + } + } + + return true; +} + +int main(int, char**) { + using namespace std::string_view_literals; + + constexpr char short_sep = ' '; + constexpr std::string_view long_sep = "12"; + + // One separator. + { + { + decltype(auto) input = "abc def"; + constexpr std::array expected = {"abc"sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12def"; + constexpr std::array expected = {"abc"sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Several separators in a row. + { + { + decltype(auto) input = "abc def"; + constexpr std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12121212def"; + constexpr std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Trailing separator. + { + { + decltype(auto) input = "abc def "; + constexpr std::array expected = {"abc"sv, "def"sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc12def12"; + constexpr std::array expected = {"abc"sv, "def"sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Leading separator. + { + { + decltype(auto) input = " abc def"; + constexpr std::array expected = {""sv, "abc"sv, "def"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12abc12def"; + constexpr std::array expected = {""sv, "abc"sv, "def"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // No separator. + { + { + decltype(auto) input = "abc"; + constexpr std::array expected = {"abc"sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "abc"; + constexpr std::array expected = {"abc"sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Empty string. + { + { + decltype(auto) input = ""; + constexpr std::array expected = {""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = ""; + constexpr std::array expected = {""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Input consisting of a single separator. + { + { + decltype(auto) input = " "; + constexpr std::array expected = {""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12"; + constexpr std::array expected = {""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Input consisting of only separators. + { + { + decltype(auto) input = " "; + constexpr std::array expected = {""sv, ""sv, ""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "121212"; + constexpr std::array expected = {""sv, ""sv, ""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Many redundant separators. + { + { + decltype(auto) input = " abc def "; + constexpr std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "1212abc121212def1212"; + constexpr std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Separators after every character. + { + { + decltype(auto) input = " a b c "; + constexpr std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, short_sep, expected)); + static_assert(test(input, short_sep, expected)); + assert(test_with_piping(input, short_sep, expected)); + static_assert(test_with_piping(input, short_sep, expected)); + } + + { + decltype(auto) input = "12a12b12c12"; + constexpr std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, long_sep, expected)); + static_assert(test(input, long_sep, expected)); + assert(test_with_piping(input, long_sep, expected)); + static_assert(test_with_piping(input, long_sep, expected)); + } + } + + // Empty separator. + { + { + constexpr std::string_view empty_sep = ""; + + decltype(auto) input = "abc"; + constexpr std::array expected = {"a"sv, "b"sv, "c"sv, ""sv}; + + assert(test(input, empty_sep, expected)); + static_assert(test(input, empty_sep, expected)); + assert(test_with_piping(input, empty_sep, expected)); + static_assert(test_with_piping(input, empty_sep, expected)); + } + } + + // Overlap between the separator and the string. + { + { + constexpr std::string_view overlapping_sep = "ab"; + + decltype(auto) input = "aabaaababb"; + constexpr std::array expected = {"a"sv, "aa"sv, ""sv, "b"sv}; + + assert(test(input, overlapping_sep, expected)); + static_assert(test(input, overlapping_sep, expected)); + assert(test_with_piping(input, overlapping_sep, expected)); + static_assert(test_with_piping(input, overlapping_sep, expected)); + } + } + + // Non-character input. + { + { + constexpr int sep = 0; + constexpr int input[] = {1, 2, 3, 0, 4, 5, 6}; + constexpr std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + + assert(test(input, sep, expected)); + static_assert(test(input, sep, expected)); + assert(test_with_piping(input, sep, expected)); + static_assert(test_with_piping(input, sep, expected)); + } + + { + constexpr std::array sep = {0, 0, 0}; + constexpr int input[] = {1, 2, 3, 0, 0, 0, 4, 5, 6}; + constexpr std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + + assert(test(input, sep, expected)); + static_assert(test(input, sep, expected)); + assert(test_with_piping(input, sep, expected)); + static_assert(test_with_piping(input, sep, expected)); + } + } + + assert(test_l_r_values()); + static_assert(test_l_r_values()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/begin.pass.cpp @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::begin() + +#include <ranges> + +#include <cassert> +#include <string_view> +#include <type_traits> +#include <utility> + +constexpr bool test() { + using View = std::ranges::lazy_split_view<std::string_view, std::string_view>; + std::string_view input = "a"; + + // Non-const. + { + View v(input, "b"); + auto val = *v.begin(); + assert(val.begin().base() == input); + } + + // Const. + { + View v(input, "b"); + const auto val = *v.begin(); + assert(val.begin().base() == input); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.default.pass.cpp @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::value_type() + +#include <ranges> + +#include <cassert> +#include <string_view> +#include <type_traits> +#include <utility> + +using View = std::ranges::lazy_split_view<std::string_view, std::string_view>; +using ValueType = decltype(*std::declval<View>().begin()); +static_assert(std::is_default_constructible_v<ValueType>); + +int main(int, char**) { + ValueType val; + (void)val; + + constexpr ValueType val2; + (void)val2; + // Note: attempting a check like `val.begin() == val.end()` leads to null pointer dereference. + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/ctor.iter.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 + +// std::ranges::lazy_split_view::outer-iterator::value_type::value_type(outer-iterator i) + +#include <ranges> + +#include <cassert> +#include <string_view> +#include <type_traits> +#include <utility> + +using View = std::ranges::lazy_split_view<std::string_view, std::string_view>; +using ValueType = decltype(*std::declval<View>().begin()); +static_assert(std::is_constructible_v<ValueType, std::ranges::iterator_t<View>>); + +constexpr bool test() { + std::string_view input = "a"; + View v(input, "b"); + ValueType val(v.begin()); + return val.begin().base() == input; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/end.pass.cpp @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// std::ranges::lazy_split_view::outer-iterator::value_type::end() + +#include <ranges> + +#include <cassert> +#include <string_view> +#include <utility> + +constexpr bool test() { + using View = std::ranges::lazy_split_view<std::string_view, std::string_view>; + std::string_view input = "a"; + + // Non-const. + { + View v(input, "b"); + auto val = *v.begin(); + static_assert(std::same_as<decltype(val.end()), std::default_sentinel_t>); + } + + // Const. + { + View v(input, "b"); + const auto val = *v.begin(); + static_assert(std::same_as<decltype(val.end()), std::default_sentinel_t>); + } + + return true; +} + +int main(int, char**) { + assert(test()); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.copy.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include <ranges> + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.current.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.current.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.current.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include <ranges> + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/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 + +// std::ranges::lazy_split_view::outer-iterator::outer-iterator() + +#include <ranges> + +#include <cassert> +#include <string_view> +#include <type_traits> +#include <utility> + +using View = std::ranges::lazy_split_view<std::string_view, std::string_view>; +using OuterIter = decltype(std::declval<View>().begin()); +static_assert(std::is_default_constructible_v<OuterIter>); + +int main(int, char**) { + OuterIter i; + (void)i; + constexpr OuterIter i2; + (void)i2; + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent.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 + +// std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent) +// requires (!forward_range<Base>) + +#include <ranges> + +#include <cassert> +#include <string_view> +#include <type_traits> +#include <utility> +#include "../types.h" + +using SplitView = std::ranges::lazy_split_view<std::string_view, std::string_view>; +using OuterIter = decltype(std::declval<SplitView>().begin()); +static_assert(std::ranges::forward_range<SplitView>); +static_assert(!std::is_constructible_v<OuterIter, const SplitView&>); + +using SplitViewInput = std::ranges::lazy_split_view<InputView, ForwardTinyView>; +using OuterIterInput = decltype(std::declval<SplitViewInput>().begin()); +// TODO(varconst): uncomment once `lazy_split_view` can use `ranges::find`. +//static_assert(!std::ranges::forward_range<SplitViewInput>); +//static_assert(std::is_constructible_v<OuterIterInput, SplitViewInput&>); + +constexpr bool test() { + InputView input; + SplitViewInput v(input, ForwardTinyView()); + + OuterIterInput i(v); + (void)v; + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/equal.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include <ranges> + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/increment.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include <ranges> + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/star.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/star.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/star.pass.cpp @@ -0,0 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +#include <ranges> + +int main(int, char**) { + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/types.compile.pass.cpp @@ -0,0 +1,40 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-no-concepts +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view::outer-iterator; + +#include <ranges> + +#include <concepts> +#include <iterator> +#include "../types.h" + +template <class Range, class Pattern> +using OuterIter = decltype(std::declval<std::ranges::lazy_split_view<Range, Pattern>>().begin()); + +// iterator_concept + +static_assert(std::same_as<typename OuterIter<ForwardView, ForwardView>::iterator_concept, std::forward_iterator_tag>); +static_assert(std::same_as<typename OuterIter<InputView, ForwardTinyView>::iterator_concept, std::input_iterator_tag>); + +// iterator_category + +static_assert(std::same_as<typename OuterIter<ForwardView, ForwardView>::iterator_category, std::input_iterator_tag>); + +template <class Range, class Pattern> +concept NoIteratorCategory = !requires { typename OuterIter<Range, Pattern>::iterator_category; }; +static_assert(NoIteratorCategory<InputView, ForwardTinyView>); + +// difference_type + +static_assert(std::same_as<typename OuterIter<ForwardView, ForwardView>::difference_type, + std::ranges::range_difference_t<ForwardView>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/types.h @@ -0,0 +1,174 @@ +//===----------------------------------------------------------------------===// +// +// 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_LAZY_SPLIT_TYPES_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_TYPES_H + +#include <ranges> +#include <string_view> +#include "test_iterators.h" + +int globalBuff[8]; + +struct MoveOnlyView : std::ranges::view_base { + std::string_view view_; + constexpr MoveOnlyView() = default; + constexpr MoveOnlyView(const char* ptr) : view_(ptr) {} + constexpr MoveOnlyView(std::string_view v) : view_(v) {} + constexpr MoveOnlyView(MoveOnlyView&&) = default; + constexpr MoveOnlyView& operator=(MoveOnlyView&&) = default; + constexpr const char* begin() const { return view_.begin(); } + constexpr const char* end() const { return view_.end(); } + constexpr bool operator==(MoveOnlyView rhs) const { return view_ == rhs.view_; } +}; +static_assert( std::ranges::view<MoveOnlyView>); +static_assert( std::ranges::contiguous_range<MoveOnlyView>); +static_assert(!std::copyable<MoveOnlyView>); + +struct CopyableView : std::ranges::view_base { + int start_; + constexpr explicit CopyableView(int start = 0) : start_(start) {} + constexpr CopyableView(CopyableView const&) = default; + constexpr CopyableView& operator=(CopyableView const&) = default; + constexpr int *begin() const { return globalBuff + start_; } + constexpr int *end() const { return globalBuff + 8; } + constexpr bool operator==(CopyableView rhs) const { return start_ == rhs.start_; } +}; + +using ForwardIter = forward_iterator<int*>; + +struct ForwardView : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardView() = default; + constexpr ForwardView(const char* ptr) : view_(ptr) {} + constexpr ForwardView(std::string_view v) : view_(v) {} + constexpr ForwardView(ForwardView&&) = default; + constexpr ForwardView& operator=(ForwardView&&) = default; + constexpr forward_iterator<const char*> begin() const { return forward_iterator<const char*>(view_.begin()); } + constexpr forward_iterator<const char*> end() const { return forward_iterator<const char*>(view_.end()); } +}; + +// Iterator types differ based on constness of this class. +struct ForwardDiffView : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardDiffView() = default; + constexpr ForwardDiffView(const char* ptr) : view_(ptr) {} + constexpr ForwardDiffView(std::string_view v) : view_(v) {} + constexpr ForwardDiffView(ForwardDiffView&&) = default; + constexpr ForwardDiffView& operator=(ForwardDiffView&&) = default; + constexpr ForwardDiffView(const ForwardDiffView&) = default; + constexpr ForwardDiffView& operator=(const ForwardDiffView&) = default; + constexpr forward_iterator<char*> begin() { return forward_iterator<char*>(nullptr); } + constexpr forward_iterator<char*> end() { return forward_iterator<char*>(nullptr); } + constexpr forward_iterator<const char*> begin() const { return forward_iterator<const char*>(view_.begin()); } + constexpr forward_iterator<const char*> end() const { return forward_iterator<const char*>(view_.end()); } +}; + +struct ForwardNonSimpleView : std::ranges::view_base { + std::string_view view_; + constexpr explicit ForwardNonSimpleView() = default; + constexpr ForwardNonSimpleView(const char* ptr) : view_(ptr) {} + constexpr ForwardNonSimpleView(std::string_view v) : view_(v) {} + constexpr ForwardNonSimpleView(ForwardNonSimpleView&&) = default; + constexpr ForwardNonSimpleView& operator=(ForwardNonSimpleView&&) = default; + constexpr forward_iterator<const char*> begin() { return forward_iterator<const char*>(view_.begin()); } + constexpr forward_iterator<const char*> end() { return forward_iterator<const char*>(view_.end()); } + constexpr cpp20_input_iterator<const char*> begin() const { return cpp20_input_iterator<const char*>(view_.begin()); } + constexpr cpp20_input_iterator<const char*> end() const { return cpp20_input_iterator<const char*>(view_.end()); } +}; + +struct ForwardTinyView : ForwardView { + ForwardTinyView() = default; + constexpr static size_t size() { return 1; } +}; + +template <class It> +class almost_forward_iterator { + It it_; + + template <class U> friend class almost_forward_iterator; + +public: + typedef std::forward_iterator_tag iterator_category; + typedef typename std::iterator_traits<It>::value_type value_type; + typedef typename std::iterator_traits<It>::difference_type difference_type; + typedef It pointer; + typedef typename std::iterator_traits<It>::reference reference; + + TEST_CONSTEXPR almost_forward_iterator() : it_() {} + TEST_CONSTEXPR explicit almost_forward_iterator(It it) : it_(it) {} + template <class U> + TEST_CONSTEXPR almost_forward_iterator(const almost_forward_iterator<U>& u) : it_(u.it_) {} + + TEST_CONSTEXPR reference operator*() const {return *it_;} + TEST_CONSTEXPR pointer operator->() const {return it_;} + + TEST_CONSTEXPR_CXX14 almost_forward_iterator& operator++() { ++it_; return *this; } + // Notice the slightly different return type. + TEST_CONSTEXPR_CXX14 const almost_forward_iterator operator++(int) {return almost_forward_iterator(it_);} + + friend TEST_CONSTEXPR bool operator==(const almost_forward_iterator& x, const almost_forward_iterator& y) {return x.it_ == y.it_;} + friend TEST_CONSTEXPR bool operator!=(const almost_forward_iterator& x, const almost_forward_iterator& y) {return x.it_ != y.it_;} + + template <class T> + void operator,(T const &) = delete; +}; + +struct ForwardOnlyIfNonConstView : std::ranges::view_base { + std::string_view view_; + + constexpr explicit ForwardOnlyIfNonConstView() = default; + constexpr ForwardOnlyIfNonConstView(const char* ptr) : view_(ptr) {} + constexpr ForwardOnlyIfNonConstView(std::string_view v) : view_(v) {} + constexpr ForwardOnlyIfNonConstView(ForwardOnlyIfNonConstView&&) = default; + constexpr ForwardOnlyIfNonConstView& operator=(ForwardOnlyIfNonConstView&&) = default; + + constexpr forward_iterator<const char*> begin() { return forward_iterator<const char*>(view_.begin()); } + constexpr forward_iterator<const char*> end() { return forward_iterator<const char*>(view_.end()); } + constexpr almost_forward_iterator<const char*> begin() const { + return almost_forward_iterator<const char*>(view_.begin()); + } + constexpr almost_forward_iterator<const char*> end() const { + return almost_forward_iterator<const char*>(view_.end()); + } +}; + +struct ThrowingDefaultCtorForwardView : std::ranges::view_base { + ThrowingDefaultCtorForwardView() noexcept(false); + ForwardIter begin() const; + ForwardIter end() const; +}; + +struct NoDefaultCtorForwardView : std::ranges::view_base { + NoDefaultCtorForwardView() = delete; + ForwardIter begin() const; + ForwardIter end() const; +}; + +struct InputView : std::ranges::view_base { + constexpr cpp20_input_iterator<int*> begin() { return cpp20_input_iterator<int*>(globalBuff); } + constexpr int* end() { return globalBuff + 8; } + constexpr cpp20_input_iterator<const int*> begin() const { return cpp20_input_iterator<const int*>(globalBuff); } + constexpr const int* end() const { return globalBuff + 8; } +}; +constexpr bool operator==(const cpp20_input_iterator<int*> &lhs, int* rhs) { return lhs.base() == rhs; } +constexpr bool operator==(int* lhs, const cpp20_input_iterator<int*> &rhs) { return rhs.base() == lhs; } + +struct ForwardRange { + ForwardIter begin() const; + ForwardIter end() const; +}; + +struct InputRange { + cpp20_input_iterator<const char*> begin() const; + std::default_sentinel_t end() const; +}; +bool operator==(const cpp20_input_iterator<const char*>&, std::default_sentinel_t); +static_assert(std::ranges::input_range<InputRange>); + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_TYPES_H diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/view_interface.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: libcpp-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view; + +#include <ranges> + +#include <cassert> +#include <concepts> + +using V = decltype(std::ranges::lazy_split_view("abc", "d")); +static_assert(std::ranges::forward_range<V>); + +// empty() +template <class View> +concept CanCallEmpty = requires (View& v) { v.empty(); }; +static_assert(CanCallEmpty<V>); + +// operator bool() +template <class View> +concept CanCallOperatorBool = requires (View& v) { static_cast<bool>(v); }; +static_assert(CanCallOperatorBool<V>); + +// front() +template <class View> +concept CanCallFront = requires (View& v) { v.front(); }; +static_assert(CanCallFront<V>); + +// size() is unavailable. +static_assert(!std::sized_sentinel_for<std::ranges::sentinel_t<V>, std::ranges::iterator_t<V>>); +template <class View> +concept CanCallSize = requires (View& v) { v.size(); }; +static_assert(!CanCallSize<V>); + +// back() is unavailable. +static_assert(!std::ranges::bidirectional_range<V>); +template <class View> +concept CanCallBack = requires (View& v) { v.back(); }; +static_assert(!CanCallBack<V>); + +// data() is unavailable. +static_assert(!std::contiguous_iterator<std::ranges::iterator_t<V>>); +template <class View> +concept CanCallData = requires (View& v) { v.data(); }; +static_assert(!CanCallData<V>); + +// operator[] is unavailable. +static_assert(!std::ranges::random_access_range<V>); +template <class View> +concept CanCallOperatorSubscript = requires (View& v) { v[0]; }; +static_assert(!CanCallOperatorSubscript<V>); + +int main(int, char**) { + // empty() + { + { + constexpr std::ranges::lazy_split_view v("abc", ""); + assert(!v.empty()); + static_assert(!v.empty()); + } + // Note: a `lazy_split_view` is never empty. + } + + // operator bool() + { + { + constexpr std::ranges::lazy_split_view v("abc", ""); + assert(v); + static_assert(v); + } + // Note: a `lazy_split_view` is never empty. + } + + // front() + { + constexpr std::ranges::lazy_split_view v("abc", ""); + assert(*(v.front()).begin() == 'a'); + static_assert(*(v.front()).begin() == 'a'); + } + + return 0; +} 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 @@ -516,6 +516,9 @@ template <class T> void operator,(T const &) = delete; }; +#ifndef _LIBCPP_HAS_NO_CONCEPTS + static_assert(std::input_iterator<cpp20_input_iterator<int*>>); +#endif template<std::input_or_output_iterator> struct iter_value_or_void { using type = void; };