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] `_,`transform_view `_,[range.all],Zoe Carver,✅ `[range.take] `_,`take_view `_,[range.all],Zoe Carver,✅ `[range.join] `_,`join_view `_,[range.all],Zoe Carver,✅ -`[range.split] `_,`split_view (renamed to lazy_split_view by P2210) `_,[range.all],Zoe Carver,In progress +`[range.split] `_,`split_view (renamed to lazy_split_view by P2210) `_,[range.all],Zoe Carver and Konstantin Varlamov,✅ `[range.counted] `_,`view::counted `_,[range.subrange],Zoe Carver,✅ `[range.common] `_,`common_view `_,[range.all],Zoe Carver,✅ `[range.reverse] `_,`reverse_view `_,[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 @@ -354,6 +354,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,429 @@ +// -*- 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/ranges_find.h> +#include <__algorithm/ranges_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 + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES) + +namespace ranges { + +template struct __require_constant; + +template +concept __tiny_range = + sized_range<_Range> && + requires { typename __require_constant::size()>; } && + (remove_reference_t<_Range>::size() <= 1); + +template + requires view<_View> && view<_Pattern> && + indirectly_comparable, iterator_t<_Pattern>, ranges::equal_to> && + (forward_range<_View> || __tiny_range<_Pattern>) +class lazy_split_view : public view_interface> { + + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + _LIBCPP_NO_UNIQUE_ADDRESS _Pattern __pattern_ = _Pattern(); + + using _MaybeCurrent = _If, __non_propagating_cache>, __empty_cache>; + _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent(); + + template struct __outer_iterator; + template struct __inner_iterator; + +public: + _LIBCPP_HIDE_FROM_ABI + lazy_split_view() + requires default_initializable<_View> && default_initializable<_Pattern> = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr lazy_split_view(_View __base, _Pattern __pattern) + : __base_(std::move(__base)), __pattern_(std::move(__pattern)) {} + + template + requires constructible_from<_View, views::all_t<_Range>> && + constructible_from<_Pattern, single_view>> + _LIBCPP_HIDE_FROM_ABI + constexpr lazy_split_view(_Range&& __r, range_value_t<_Range> __e) + : __base_(views::all(std::forward<_Range>(__r))) + // TODO(var-const): use `views::single` once it's implemented. + , __pattern_(ranges::single_view(std::move(__e))) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr _View base() const& requires copy_constructible<_View> { return __base_; } + _LIBCPP_HIDE_FROM_ABI + constexpr _View base() && { return std::move(__base_); } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() { + if constexpr (forward_range<_View>) { + return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::begin(__base_)}; + } else { + __current_.__emplace(ranges::begin(__base_)); + return __outer_iterator{*this}; + } + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto begin() const requires forward_range<_View> && forward_range { + return __outer_iterator{*this, ranges::begin(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() requires forward_range<_View> && common_range<_View> { + return __outer_iterator<__simple_view<_View> && __simple_view<_Pattern>>{*this, ranges::end(__base_)}; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr auto end() const { + if constexpr (forward_range<_View> && forward_range && common_range) { + return __outer_iterator{*this, ranges::end(__base_)}; + } else { + return default_sentinel; + } + } + +private: + + template + struct __outer_iterator_category {}; + + template + struct __outer_iterator_category<_Tp> { + using iterator_category = input_iterator_tag; + }; + + template + struct __outer_iterator : __outer_iterator_category<__maybe_const<_Const, _View>> { + private: + template + friend struct __inner_iterator; + friend __outer_iterator; + + using _Parent = __maybe_const<_Const, lazy_split_view>; + using _Base = __maybe_const<_Const, _View>; + + _Parent* __parent_ = nullptr; + using _MaybeCurrent = _If, iterator_t<_Base>, __empty_cache>; + _LIBCPP_NO_UNIQUE_ADDRESS _MaybeCurrent __current_ = _MaybeCurrent(); + bool __trailing_empty_ = false; + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto& __current() noexcept { + if constexpr (forward_range<_View>) { + return __current_; + } else { + return *__parent_->__current_; + } + } + + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr const auto& __current() const noexcept { + if constexpr (forward_range<_View>) { + return __current_; + } else { + return *__parent_->__current_; + } + } + + public: + // using iterator_category = inherited; + using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; + using difference_type = range_difference_t<_Base>; + + struct value_type : view_interface { + private: + __outer_iterator __i_ = __outer_iterator(); + + public: + _LIBCPP_HIDE_FROM_ABI + value_type() = default; + _LIBCPP_HIDE_FROM_ABI + constexpr explicit value_type(__outer_iterator __i) + : __i_(std::move(__i)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __inner_iterator<_Const> begin() const { return __inner_iterator<_Const>{__i_}; } + _LIBCPP_HIDE_FROM_ABI + constexpr default_sentinel_t end() const noexcept { return default_sentinel; } + }; + + _LIBCPP_HIDE_FROM_ABI + __outer_iterator() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __outer_iterator(_Parent& __parent) + requires (!forward_range<_Base>) + : __parent_(std::addressof(__parent)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __outer_iterator(_Parent& __parent, iterator_t<_Base> __current) + requires forward_range<_Base> + : __parent_(std::addressof(__parent)), __current_(std::move(__current)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr __outer_iterator(__outer_iterator __i) + requires _Const && convertible_to, iterator_t<_Base>> + : __parent_(__i.__parent_), __current_(std::move(__i.__current_)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr value_type operator*() const { return value_type{*this}; } + + _LIBCPP_HIDE_FROM_ABI + 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) { + // Empty pattern: split on every element in the input range + ++__current(); + + } else if constexpr (__tiny_range<_Pattern>) { + // One-element pattern: we can use `ranges::find`. + __current() = ranges::find(std::move(__current()), __end, *__pbegin); + if (__current() != __end) { + // Make sure we point to after the separator we just found. + ++__current(); + if (__current() == __end) + __trailing_empty_ = true; + } + + } else { + // General case for n-element pattern. + do { + const auto [__b, __p] = ranges::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; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Base>) { + auto __tmp = *this; + ++*this; + return __tmp; + + } else { + ++*this; + } + } + + _LIBCPP_HIDE_FROM_ABI + 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_; + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __outer_iterator& __x, default_sentinel_t) { + return __x.__current() == ranges::end(__x.__parent_->__base_) && !__x.__trailing_empty_; + } + }; + + template + struct __inner_iterator_category {}; + + template + struct __inner_iterator_category<_Tp> { + using iterator_category = _If< + derived_from>::iterator_category, forward_iterator_tag>, + forward_iterator_tag, + typename iterator_traits>::iterator_category + >; + }; + + template + struct __inner_iterator : __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_category = inherited; + using iterator_concept = typename __outer_iterator<_Const>::iterator_concept; + using value_type = range_value_t<_Base>; + using difference_type = range_difference_t<_Base>; + + _LIBCPP_HIDE_FROM_ABI + __inner_iterator() = default; + + _LIBCPP_HIDE_FROM_ABI + constexpr explicit __inner_iterator(__outer_iterator<_Const> __i) + : __i_(std::move(__i)) {} + + _LIBCPP_HIDE_FROM_ABI + constexpr const iterator_t<_Base>& base() const& noexcept { return __i_.__current(); } + _LIBCPP_HIDE_FROM_ABI + constexpr iterator_t<_Base> base() && + requires forward_range<_View> { return std::move(__i_.__current()); } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator*() const { return *__i_.__current(); } + + _LIBCPP_HIDE_FROM_ABI + constexpr __inner_iterator& operator++() { + __incremented_ = true; + + if constexpr (!forward_range<_Base>) { + if constexpr (_Pattern::size() == 0) { + return *this; + } + } + + ++__i_.__current(); + return *this; + } + + _LIBCPP_HIDE_FROM_ABI + constexpr decltype(auto) operator++(int) { + if constexpr (forward_range<_Base>) { + auto __tmp = *this; + ++*this; + return __tmp; + + } else { + ++*this; + } + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __inner_iterator& __x, const __inner_iterator& __y) + requires forward_range<_Base> { + return __x.__i_.__current() == __y.__i_.__current(); + } + + _LIBCPP_HIDE_FROM_ABI + friend constexpr bool operator==(const __inner_iterator& __x, default_sentinel_t) { + auto [__pcur, __pend] = ranges::subrange{__x.__i_.__parent_->__pattern_}; + 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; + } + } + + _LIBCPP_HIDE_FROM_ABI + 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()); + } + + _LIBCPP_HIDE_FROM_ABI + 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> { + ranges::iter_swap(__x.__i_.__current(), __y.__i_.__current()); + } + }; + +}; + +template +lazy_split_view(_Range&&, _Pattern&&) -> lazy_split_view, views::all_t<_Pattern>>; + +template +lazy_split_view(_Range&&, range_value_t<_Range>) + -> lazy_split_view, single_view>>; + +namespace views { +namespace __lazy_split_view { +struct __fn : __range_adaptor_closure<__fn> { + template + [[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 + requires constructible_from, _Pattern> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI + constexpr auto operator()(_Pattern&& __pattern) const + noexcept(is_nothrow_constructible_v, _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 // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_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 @@ -828,6 +828,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 requires view && input_range> class join_view; + + // [range.lazy.split], lazy split view + template + concept tiny-range = see below; // exposition only + + template + requires view && view && + indirectly_comparable, iterator_t, ranges::equal_to> && + (forward_range || tiny-range) + 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/libcxx/ranges/range.adaptors/range.lazy.split/no_unique_address.compile.pass.cpp b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/no_unique_address.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/ranges/range.adaptors/range.lazy.split/no_unique_address.compile.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-has-no-incomplete-ranges + +// clang-cl and cl currently don't support [[no_unique_address]] +// XFAIL: msvc + +// class lazy_split_view { +// _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); +// _LIBCPP_NO_UNIQUE_ADDRESS _Pattern __pattern_ = _Pattern(); +// }; + +#include + +#include +#include "test_iterators.h" + +// Verify the optimization that, if `View` is a forward range, the `lazy_split_view` itself doesn't store the `current` +// iterator (instead, it's stored in the `outer-iterator`). +// +// Note that the Standard marks all data members of `lazy_split_view` as "exposition only", so this test has to be +// libc++-specific. +namespace test1 { + + using SplitView = std::ranges::lazy_split_view; + // The `lazy_split_view` only stores the `View` and the `Pattern`, not an iterator. + static_assert(sizeof(SplitView) == sizeof(std::string_view) * 2); + +} // namespace test1 + +// Verify the optimization that, if `View` is an input range, the `outer-iterator` doesn't store the `current` iterator +// (instead, it's stored in the `lazy_split_view` itself). +// +// Note that the Standard marks all data members of `outer-iterator` as "exposition only", so this test has to be +// libc++-specific. +namespace test2 { + + struct InputView : std::ranges::view_base { + int x; + cpp20_input_iterator begin() const; + sentinel_wrapper> end() const; + }; + static_assert( std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert( std::ranges::view); + + struct TinyView : std::ranges::view_base { + int x; + int* begin() const; + int* end() const; + constexpr static size_t size() { return 1; } + }; + static_assert( std::ranges::forward_range); + static_assert( std::ranges::__tiny_range); + static_assert( std::ranges::view); + + using SplitView = std::ranges::lazy_split_view; + using OuterIter = std::ranges::iterator_t; + // The `outer-iterator` only stores a pointer to the parent and a boolean (aligned to the size of a pointer), not an + // iterator. + static_assert(sizeof(OuterIter) == sizeof(void*) * 2); + +} // namespace test2 + +// Verify the libc++-specific optimization that empty `View` and `Pattern` use the `[[no_unique_address]]` attribute. +// Both `View` and `Pattern` are forward views. +namespace test3 { + + struct EmptyView1 : std::ranges::view_base { + int* begin() const; + int* end() const; + }; + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + + // Note: it's important to inherit `EmptyView1` and `EmptyView2` from different bases, otherwise they still cannot share + // the same address regardless of whether `[[no_unique_address]]` is used. + struct EmptyView2 : std::ranges::view_interface { + int* begin() const; + int* end() const; + }; + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + + static_assert(sizeof(std::ranges::lazy_split_view) == 1); + +} // namespace test3 + +// Verify the libc++-specific optimization that empty `View` and `Pattern` use the `[[no_unique_address]]` attribute. +// `View` is an input view and `Pattern` is a tiny view. +namespace test4 { + + struct EmptyInputView : std::ranges::view_base { + cpp20_input_iterator begin() const; + sentinel_wrapper> end() const; + }; + static_assert( std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert( std::ranges::view); + + struct EmptyTinyView : std::ranges::view_base { + int* begin() const; + int* end() const; + constexpr static size_t size() { return 1; } + }; + static_assert( std::ranges::forward_range); + static_assert( std::ranges::__tiny_range); + static_assert( std::ranges::view); + + static_assert(sizeof(std::ranges::lazy_split_view) == + sizeof(std::ranges::__non_propagating_cache>)); + +} // namespace test4 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,128 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// std::views::lazy_split + +#include + +#include +#include +#include +#include +#include + +#include "test_iterators.h" +#include "types.h" + +template +concept CanBePiped = requires (View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; +}; + +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(); } +}; + +struct NotAView { }; + +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert( std::is_invocable_v); + +static_assert( CanBePiped); +static_assert( CanBePiped); +static_assert(!CanBePiped); +static_assert(!CanBePiped); + +static_assert(std::same_as); + +constexpr bool test() { + std::string_view input = "abc"; + 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; + std::same_as decltype(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; + std::same_as decltype(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; + std::same_as decltype(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::string_view>; + std::same_as decltype(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, decltype(f)>; + std::same_as decltype(auto) result = partial(view); + assert(result.base().base().begin() == input.begin()); + assert(result.base().base().end() == input.end()); + } + + // Test that one can call `std::views::lazy_split` with arbitrary stuff, as long as we + // don't try to actually complete the call by passing it a range. + // + // That makes no sense and we can't do anything with the result, but it's valid. + { + struct X { }; + [[maybe_unused]] auto partial = std::views::lazy_split(X{}); + } + + 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,106 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// constexpr View base() const& requires copy_constructible<_View>; +// constexpr View base() &&; + +#include + +#include +#include +#include +#include "types.h" + +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); +static_assert( std::ranges::contiguous_range); +static_assert(!std::copyable); + +template +concept CanCallBase = requires(View v) { std::forward(v).base(); }; + +static_assert( CanCallBase&>); +static_assert( CanCallBase const &>); +static_assert( CanCallBase&&>); +static_assert( CanCallBase const &&>); +static_assert(!CanCallBase&>); +static_assert(!CanCallBase const &>); +static_assert( CanCallBase&&>); +static_assert(!CanCallBase const &&>); + +struct View : std::ranges::view_base { + enum class InitializedBy { + Copy, + Move, + Invalid + }; + + std::string_view v_; + InitializedBy initialized_by = InitializedBy::Invalid; + constexpr View(std::string_view v) : v_(v) {} + + constexpr auto begin() const { return v_.begin(); } + constexpr auto end() const { return v_.end(); } + + constexpr View(const View& rhs) : v_(rhs.v_) { initialized_by = InitializedBy::Copy; } + constexpr View(View&& rhs) : v_(rhs.v_) { initialized_by = InitializedBy::Move; } + constexpr View& operator=(const View& rhs) = default; + constexpr View& operator=(View&& rhs) = default; + constexpr bool operator==(const View& rhs) const { return v_ == rhs.v_; } +}; + +constexpr bool test() { + // Copyable input -- both lvalue and rvalue overloads of `base` are available. + { + { + View str("abc def"); + std::ranges::lazy_split_view v(str, " "); + + std::same_as decltype(auto) result = v.base(); + assert(result == str); + assert(result.initialized_by == View::InitializedBy::Copy); + } + + { + View str("abc def"); + std::ranges::lazy_split_view v(str, " "); + + std::same_as decltype(auto) result = std::move(v).base(); + assert(result == str); + assert(result.initialized_by == View::InitializedBy::Move); + } + } + + // Move-only input -- only the rvalue overload of `base` is available. + { + std::ranges::lazy_split_view 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,138 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// constexpr auto begin(); +// constexpr auto begin() const requires forward_range && forward_range; + +#include + +#include +#include +#include "test_iterators.h" +#include "types.h" + +template +concept OuterIterConceptSameAs = + std::same_as::iterator_concept, Expected>; + +template +concept InnerIterValueTypeSameAs = requires (View v) { + { *(*v.begin()).begin() } -> std::same_as; +}; + +template +concept ConstBeginDisabled = !requires (const View v) { + { (*v.begin()) }; +}; + +constexpr bool test() { + // non-const: forward_range && simple-view -> outer-iterator + // const: forward_range && forward_range -> outer-iterator + { + using V = ForwardView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !simple-view -> outer-iterator + // const: forward_range && forward_range -> outer-iterator + { + using V = ForwardDiffView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !simple-view -> outer-iterator + // const: forward_range && !forward_range -> disabled + { + using V = ForwardOnlyIfNonConstView; + using P = V; + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + static_assert(ConstBeginDisabled); + } + + // non-const: forward_range && simple-view && !simple-view -> outer-iterator + // const: forward_range && forward_range -> outer-iterator + { + using V = ForwardView; + using P = ForwardOnlyIfNonConstView; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: !forward_range && tiny-range -> outer-iterator + // const: !forward_range -> disabled + { + using V = InputView; + using P = ForwardTinyView; + + static_assert(!std::ranges::forward_range); + static_assert(std::ranges::forward_range

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + static_assert(ConstBeginDisabled); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/constraints.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/constraints.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/constraints.compile.pass.cpp @@ -0,0 +1,210 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// template +// requires view && view && +// indirectly_comparable, iterator_t, ranges::equal_to> && +// (forward_range || tiny-range) +// class lazy_split_view; + +#include + +#include "test_iterators.h" +#include "types.h" + +struct ForwardRange { + forward_iterator begin() const; + forward_iterator end() const; +}; +static_assert( std::ranges::forward_range); + +template +concept CanInstantiate = requires { + typename std::ranges::lazy_split_view; +}; + +// All constraints satisfied (`View` and `Pattern` are forward views). +namespace test1 { + + using View = ForwardView; + using Pattern = ForwardView; + static_assert( std::ranges::forward_range); + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert( CanInstantiate); + +} // namespace test1 + +// All constraints satisfied (`View` is an input view and `Pattern` is a tiny view). +namespace test2 { + + using View = InputView; + using Pattern = ForwardTinyView; + static_assert( std::ranges::input_range); + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert( CanInstantiate); + +} // namespace test2 + +// `View` is not an input range. +namespace test3 { + + struct AlmostInputIterator { + using value_type = char; + using difference_type = ptrdiff_t; + using iterator_concept = int; + + constexpr const char& operator*() const; + constexpr AlmostInputIterator& operator++(); + constexpr void operator++(int); + constexpr bool operator==(const AlmostInputIterator&) const; + }; + + static_assert( std::input_or_output_iterator); + static_assert(!std::input_iterator); + + struct NonInputView : std::ranges::view_base { + AlmostInputIterator begin() const; + AlmostInputIterator end() const; + }; + + using View = NonInputView; + using Pattern = ForwardTinyView; + static_assert(!std::ranges::input_range); + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test3 + +// `View` is not a view. +namespace test4 { + + using View = ForwardRange; + using Pattern = ForwardView; + static_assert( std::ranges::input_range); + static_assert( std::ranges::forward_range); + static_assert(!std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test4 + +// `Pattern` is not a forward range. +namespace test5 { + + using View = ForwardView; + using Pattern = InputView; + static_assert( std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test5 + +// Not indirectly comparable. +namespace test6 { + + struct Empty{}; + struct IntForwardView : std::ranges::view_base { + constexpr forward_iterator begin() const { return {}; } + constexpr forward_iterator end() const { return {}; } + }; + + using View = ForwardView; + using Pattern = IntForwardView; + static_assert( std::ranges::input_range); + static_assert( std::ranges::forward_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert(!std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test6 + +// `View` is an input range and `Pattern` is not a tiny range. +namespace test7 { + + using View = InputView; + using Pattern = ForwardView; + static_assert( std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert( std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__tiny_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test7 + +// `View` is an input range and `Pattern` is almost a tiny range, except the `size()` function is not `constexpr`. +namespace test8 { + + struct AlmostTinyRange : std::ranges::view_base { + int* begin() const; + int* end() const; + static size_t size() { return 1; } + }; + + using View = InputView; + using Pattern = AlmostTinyRange; + static_assert( std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert( std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__tiny_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test8 + +// `View` is an input range and `Pattern` is almost a tiny range, except the `size()` returns a number `>2`. +namespace test9 { + + struct AlmostTinyRange : std::ranges::view_base { + int* begin() const; + int* end() const; + constexpr static size_t size() { return 2; } + }; + + using View = InputView; + using Pattern = ForwardView; + static_assert( std::ranges::input_range); + static_assert(!std::ranges::forward_range); + static_assert( std::ranges::forward_range); + LIBCPP_STATIC_ASSERT(!std::ranges::__tiny_range); + static_assert( std::ranges::view); + static_assert( std::ranges::view); + static_assert( std::indirectly_comparable< + std::ranges::iterator_t, std::ranges::iterator_t, std::ranges::equal_to>); + static_assert(!CanInstantiate); + +} // namespace test9 diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/copy.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/copy.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/copy.pass.cpp @@ -0,0 +1,90 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view; + +#include + +#include +#include +#include +#include "small_string.h" +#include "types.h" + +constexpr bool operator==(const InputView& lhs, const InputView& rhs) { + return SmallString(lhs) == SmallString(rhs); +} + +constexpr bool test() { + // Can copy `lazy_split_view`. + { + // Forward range. + { + std::ranges::lazy_split_view v1("abc def", " "); + auto v2 = v1; + assert(v2.base() == v1.base()); + } + + // Input range. + { + SplitViewInput v1("abc def", ' '); + auto v2 = v1; + assert(v2.base() == v1.base()); + } + } + + // Can move `lazy_split_view`. + { + // Forward range. + { + std::string_view base = "abc def"; + std::ranges::lazy_split_view v1(base, " "); + auto v2 = std::move(v1); + assert(v2.base() == base); + } + + // Input range. + { + InputView base("abc def"); + SplitViewInput v1(base, ' '); + auto v2 = std::move(v1); + assert(v2.base() == base); + } + } + + // `non-propagating-cache` is not copied. + { + SplitViewInput v1("abc def ghi", ' '); + auto outer_iter1 = v1.begin(); + ++outer_iter1; + auto val1 = *outer_iter1; + auto i1 = val1.begin(); + assert(*i1 == 'd'); + ++i1; + assert(*i1 == 'e'); + + auto v2 = v1; + auto val2 = *v2.begin(); + auto i2 = val2.begin(); + assert(*i2 == 'a'); + ++i2; + assert(*i2 == 'b'); + } + + 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,66 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// template +// lazy_split_view(R&&, P&&) -> lazy_split_view, views::all_t

>; +// +// template +// lazy_split_view(R&&, range_value_t) -> lazy_split_view, single_view>>; + +#include + +#include +#include +#include +#include "types.h" + +struct ForwardRange { + forward_iterator begin() const; + forward_iterator end() const; +}; +static_assert( std::ranges::forward_range); + +struct InputRange { + cpp20_input_iterator begin() const; + std::default_sentinel_t end() const; +}; +bool operator==(const cpp20_input_iterator&, std::default_sentinel_t); +static_assert(std::ranges::input_range); + +template +constexpr void test() { + I1 i1{}; + I2 i2{}; + + std::ranges::lazy_split_view v(std::move(i1), std::move(i2)); + static_assert(std::same_as>); + using O = decltype(std::move(v).base()); + static_assert(std::same_as); +} + +constexpr void testCtad() { + // (Range, Pattern) + test(); + test, std::ranges::views::all_t>(); + + // (Range, RangeElement) + test, std::ranges::single_view>(); + test, std::ranges::single_view>(); + + // (Range, RangeElement) with implicit conversion. + test, std::ranges::single_view>(); + test, std::ranges::single_view>(); + + // Note: CTAD from (InputRange, ForwardTinyRange) doesn't work -- the deduction guide wraps the pattern in + // `views::all_t`, resulting in `views::owning_view`. That type would never satisfy `tiny-range` + // because `views::owning_view` contains a member function `size()` that shadows the static `size()` in + // `ForwardTinyRange`. +} 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,59 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// lazy_split_view() requires default_initializable && default_initializable

= default; + +#include + +#include +#include "types.h" + +struct ThrowingDefaultCtorForwardView : std::ranges::view_base { + ThrowingDefaultCtorForwardView() noexcept(false); + forward_iterator begin() const; + forward_iterator end() const; +}; + +struct NoDefaultCtorForwardView : std::ranges::view_base { + NoDefaultCtorForwardView() = delete; + forward_iterator begin() const; + forward_iterator end() const; +}; + +static_assert( std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); +static_assert(!std::is_default_constructible_v>); + +static_assert( std::is_nothrow_default_constructible_v>); +static_assert(!std::is_nothrow_default_constructible_v); +static_assert(!std::is_nothrow_default_constructible_v< + std::ranges::lazy_split_view>); + +constexpr bool test() { + { + std::ranges::lazy_split_view v; + assert(v.base() == CopyableView()); + } + + { + std::ranges::lazy_split_view 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,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-has-no-incomplete-ranges + +// template +// requires constructible_from> && +// constructible_from>> +// constexpr lazy_split_view(Range&& r, range_value_t e); + +#include + +#include +#include +#include +#include +#include "types.h" + +constexpr bool test() { + decltype(auto) str = "abc def"; + std::ranges::lazy_split_view v(str, ' '); + assert(v.base() == std::string_view(str, sizeof(str))); + + { + using V = std::ranges::lazy_split_view; + static_assert( std::is_constructible_v); + static_assert( std::is_constructible_v); + struct Empty {}; + static_assert(!std::is_constructible_v); + static_assert(!std::is_constructible_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/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,32 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// constexpr lazy_split_view(View base, Pattern pattern); + +#include + +#include +#include + +constexpr bool test() { + std::string_view str = "abc def"; + std::ranges::lazy_split_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,152 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// constexpr auto end() requires forward_range && common_range; +// constexpr auto end() const; + +#include + +#include +#include +#include "test_iterators.h" +#include "types.h" + +template +concept OuterIterConceptSameAs = + std::same_as().end())::iterator_concept, Expected>; + +template +concept InnerIterValueTypeSameAs = requires (View v) { + { *(*v.end()).begin() } -> std::same_as; +}; + +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 begin() { return forward_iterator(nullptr); } + constexpr std::default_sentinel_t end() { return std::default_sentinel; } + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } +}; +bool operator==(forward_iterator, 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 begin() { return forward_iterator(nullptr); } + constexpr std::default_sentinel_t end() { return std::default_sentinel; } + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr std::default_sentinel_t end() const { return std::default_sentinel; } +}; +bool operator==(forward_iterator, std::default_sentinel_t) { return false; } + +constexpr bool test() { + // non-const: forward_range && simple_view && simple_view

-> outer-iterator + // const: forward_range && common_range -> outer-iterator + { + using V = ForwardView; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view

); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && common_range && simple_view && !simple_view

-> outer-iterator + // const: forward_range && forward_range && common_range -> outer-iterator + { + using V = ForwardView; + using P = ForwardDiffView; + + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + LIBCPP_STATIC_ASSERT(std::ranges::__simple_view); + LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view

); + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !common_range -> disabled + // const: forward_range && forward_range && common_range -> outer-iterator + { + using V = ForwardViewCommonIfConst; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::forward_range); + static_assert(std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + + const std::ranges::lazy_split_view cv; + static_assert(OuterIterConceptSameAs); + static_assert(InnerIterValueTypeSameAs); + } + + // non-const: forward_range && !common_range -> disabled + // const: forward_range && forward_range && !common_range -> outer-iterator + { + using V = ForwardViewNonCommonRange; + using P = V; + + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + static_assert(std::ranges::forward_range); + static_assert(!std::ranges::common_range); + + std::ranges::lazy_split_view v; + static_assert(std::same_as); + + const std::ranges::lazy_split_view cv; + 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.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,356 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// template +// class std::ranges::lazy_split_view; + +#include + +#include +#include +#include +#include +#include +#include +#include "small_string.h" + +template +constexpr bool is_equal(const View& v, const Expected& expected) { + using Char = std::ranges::range_value_t>; + using Str = BasicSmallString; + + return std::equal(v.begin(), v.end(), expected.begin(), expected.end(), + [](const auto& split_value, const auto& expected_value) { + return Str(split_value) == Str(expected_value); + }); +} + +template +constexpr bool test_function_call(T&& input, Separator&& separator, std::array expected) { + std::ranges::lazy_split_view v(input, separator); + + return is_equal(v, expected); +} + +template +constexpr bool test_with_piping(T&& input, Separator&& separator, std::array expected) { + auto expected_it = expected.begin(); + for (auto e : input | std::ranges::views::lazy_split(separator)) { + if (expected_it == expected.end()) + return false; + if (SmallString(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. + { + auto input = "abc"sv; + auto sep = " "sv; + [[maybe_unused]] std::ranges::lazy_split_view v(input, sep); + } + + // Const lvalues. + { + const auto input = "abc"sv; + const auto sep = " "sv; + [[maybe_unused]] std::ranges::lazy_split_view v(input, sep); + } + + // Rvalues. + { + auto input = "abc"sv; + auto sep = " "sv; + [[maybe_unused]] std::ranges::lazy_split_view v(std::move(input), std::move(sep)); + } + + // Const rvalues. + { + const auto input = "abc"sv; + const auto sep = " "sv; + [[maybe_unused]] std::ranges::lazy_split_view v(std::move(input), std::move(sep)); + } + } + + return true; +} + +constexpr bool test_string_literal_separator() { + // Splitting works as expected when the separator is a single character literal. + { + std::ranges::lazy_split_view v("abc def", ' '); + assert(is_equal(v, std::array{"abc", "def"})); + } + + // Counterintuitively, a seemingly equivalent separator expressed as a string literal doesn't match anything. This is + // because of the implicit terminating null in the literal. + { + std::ranges::lazy_split_view v("abc def", " "); + assert(is_equal(v, std::array{"abc def"})); + } + + // To illustrate the previous point further, the separator is actually a two-character string literal: `{' ', '\0'}`. + // Should the input string contain that two-character sequence, the separator would match. + { + std::ranges::lazy_split_view v("abc \0def", " "); + assert(is_equal(v, std::array{"abc", "def"})); + } + + return true; +} + + +// Make sure that a string literal and a `string_view` produce the same results (which isn't always the case, see +// below). +template +constexpr std::string_view sv(T&& str) { + return std::string_view(str); +}; + +template +constexpr void test_one(T&& input, Separator&& separator, std::array expected) { + assert(test_function_call(input, separator, expected)); + assert(test_function_call(sv(input), separator, expected)); + assert(test_with_piping(input, separator, expected)); + assert(test_with_piping(sv(input), separator, expected)); +} + +constexpr bool test() { + using namespace std::string_view_literals; + + char short_sep = ' '; + auto long_sep = "12"sv; + + // One separator. + { + std::array expected = {"abc"sv, "def"sv}; + test_one("abc def", short_sep, expected); + test_one("abc12def", long_sep, expected); + } + + // Several separators in a row. + { + std::array expected = {"abc"sv, ""sv, ""sv, ""sv, "def"sv}; + test_one("abc def", short_sep, expected); + test_one("abc12121212def", long_sep, expected); + } + + // Trailing separator. + { + std::array expected = {"abc"sv, "def"sv, ""sv}; + test_one("abc def ", short_sep, expected); + test_one("abc12def12", long_sep, expected); + } + + // Leading separator. + { + std::array expected = {""sv, "abc"sv, "def"sv}; + test_one(" abc def", short_sep, expected); + test_one("12abc12def", long_sep, expected); + } + + // No separator. + { + std::array expected = {"abc"sv}; + test_one("abc", short_sep, expected); + test_one("abc", long_sep, expected); + } + + // Empty string. + { + // Note: this is one of the edge cases where a built-in string literal and a `string_view` literal would produce + // different results. This is essentially because `size(""sv)` is `0` whereas `size("")` is `1` (accounting for the + // terminating null). Thus, the terminating null is ignored for a `string_view` but treated as a separate character + // for a built-in string literal (for `string_view` to behave identically, its `(ptr, size)` constructor would have + // to be used). + { + decltype(auto) input = ""; + std::array expected_literal = {""sv}; + std::array expected_sv = {}; + + assert(test_function_call(input, short_sep, expected_literal)); + assert(test_function_call(sv(input), short_sep, expected_sv)); + assert(test_with_piping(input, short_sep, expected_literal)); + assert(test_with_piping(sv(input), short_sep, expected_sv)); + } + + { + decltype(auto) input = ""; + std::array expected_literal = {""sv}; + std::array expected_sv = {}; + + assert(test_function_call(input, long_sep, expected_literal)); + assert(test_function_call(sv(input), long_sep, expected_sv)); + assert(test_with_piping(input, long_sep, expected_literal)); + assert(test_with_piping(sv(input), long_sep, expected_sv)); + } + } + + // Input consisting of a single separator. + { + std::array expected = {""sv, ""sv}; + test_one(" ", short_sep, expected); + test_one("12", long_sep, expected); + } + + // Input consisting of only separators. + { + std::array expected = {""sv, ""sv, ""sv, ""sv}; + test_one(" ", short_sep, expected); + test_one("121212", long_sep, expected); + } + + // The separator and the string use the same character only. + { + auto overlapping_sep = "aaa"sv; + std::array expected = {""sv, "aa"sv}; + test_one("aaaaa", overlapping_sep, expected); + } + + // Many redundant separators. + { + std::array expected = {""sv, ""sv, "abc"sv, ""sv, ""sv, "def"sv, ""sv, ""sv}; + test_one(" abc def ", short_sep, expected); + test_one("1212abc121212def1212", long_sep, expected); + } + + // Separators after every character. + { + std::array expected = {""sv, "a"sv, "b"sv, "c"sv, ""sv}; + test_one(" a b c ", short_sep, expected); + test_one("12a12b12c12", long_sep, expected); + } + + // Empty separator. + { + // Note: this is one of the edge cases where a built-in string literal and a `string_view` literal would produce + // different results. This is essentially because `size(""sv)` is `0` whereas `size("")` is `1` (accounting for the + // terminating null). Thus, the terminating null is ignored for a `string_view` but treated as a separate character + // for a built-in string literal (for `string_view` to behave identically, its `(ptr, size)` constructor would have + // to be used). + { + auto empty_sep = ""sv; + + decltype(auto) input = "abc"; + std::array expected_literal = {"a"sv, "b"sv, "c"sv, ""sv}; + std::array expected_sv = {"a"sv, "b"sv, "c"sv}; + + assert(test_function_call(input, empty_sep, expected_literal)); + assert(test_function_call(sv(input), empty_sep, expected_sv)); + assert(test_with_piping(input, empty_sep, expected_literal)); + assert(test_with_piping(sv(input), empty_sep, expected_sv)); + } + } + + // Terminating null as a separator. + { + { + auto null_sep = "\0"sv; + + decltype(auto) input = "abc\0def"; + // The empty segment at the end is because the implicit terminating null in the string literal is also treated as + // a separator. + std::array expected = {"abc"sv, "def"sv, ""sv}; + + assert(test_function_call(input, null_sep, expected)); + // Note: the `sv` helper uses the `const char*` constructor of `string_view` which would treat the embedded + // terminating null as the end of the string. + assert(test_function_call(std::string_view(input, sizeof(input)), null_sep, expected)); + assert(test_with_piping(input, null_sep, expected)); + assert(test_with_piping(std::string_view(input, sizeof(input)), null_sep, expected)); + } + + { + auto null_sep = "\0\0"sv; + + decltype(auto) input = "abc\0\0def"; + // Unlike the case above where the separator is a single terminating null, there is no trailing empty segment in + // the result. That's because the implicit terminating null in the string literal is a single character, whereas + // the separator in this test case is two null characters. + std::array expected = {"abc"sv, "def"sv}; + + assert(test_function_call(input, null_sep, expected)); + // Note: the `sv` helper uses the `const char*` constructor of `string_view` which would treat the embedded + // terminating null as the end of the string. + assert(test_function_call(std::string_view(input, sizeof(input)), null_sep, expected)); + assert(test_with_piping(input, null_sep, expected)); + assert(test_with_piping(std::string_view(input, sizeof(input)), null_sep, expected)); + } + } + + // Overlap between the separator and the string (see https://wg21.link/lwg3505). + { + auto overlapping_sep = "ab"sv; + std::array expected = {"a"sv, "aa"sv, ""sv, "b"sv}; + test_one("aabaaababb", overlapping_sep, expected); + } + + // Different character types. + { + // `char`. + test_function_call("abc def", ' ', std::array{"abc", "def"}); +#ifndef TEST_HAS_NO_WIDE_CHARACTERS + // `wchar_t`. + test_function_call(L"abc def", L' ', std::array{L"abc", L"def"}); +#endif + // `char8_t`. + test_function_call(u8"abc def", u8' ', std::array{u8"abc", u8"def"}); + // `char16_t`. + test_function_call(u"abc def", u' ', std::array{u"abc", u"def"}); + // `char32_t`. + test_function_call(U"abc def", U' ', std::array{U"abc", U"def"}); + } + + // Non-character input. + { + { + int sep = 0; + int input[] = {1, 2, 3, 0, 4, 5, 6}; + std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + + assert(test_function_call(input, sep, expected)); + assert(test_with_piping(input, sep, expected)); + } + + { + std::array sep = {0, 0, 0}; + int input[] = {1, 2, 3, 0, 0, 0, 4, 5, 6}; + std::array expected = {std::array{1, 2, 3}, std::array{4, 5, 6}}; + + assert(test_function_call(input, sep, expected)); + assert(test_with_piping(input, sep, expected)); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + test_l_r_values(); + static_assert(test_l_r_values()); + + test_string_literal_separator(); + static_assert(test_string_literal_separator()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/base.pass.cpp @@ -0,0 +1,78 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// constexpr const iterator_t& inner-iterator::base() const& noexcept; +// +// constexpr iterator_t inner-iterator::base() && +// requires forward_range; + +#include + +#include +#include +#include "../types.h" + +template +concept CanCallBase = requires(InnerIter i) { + { std::forward(i).base() } -> std::same_as; +}; + +static_assert( CanCallBase); +static_assert( CanCallBase); +static_assert( CanCallBase); +static_assert( CanCallBase); + +static_assert( CanCallBase); +static_assert( CanCallBase); +static_assert( CanCallBase); +static_assert( CanCallBase); + +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); +static_assert( noexcept(std::declval().base())); + +constexpr bool test() { + // `base` works with a forward view (two different overloads based on ref-qualification of the `inner-iterator`). + { + using BaseIter = std::ranges::iterator_t; + CopyableView b("abc def"); + std::ranges::lazy_split_view v(b, " "); + + auto i = (*v.begin()).begin(); + static_assert(std::same_as); + assert(i.base() == b.begin()); + static_assert(std::same_as); + assert(std::move(i).base() == b.begin()); + } + + // `base` works with an input view (no overloads). + { + using BaseIter = std::ranges::iterator_t; + InputView b("abc def"); + std::ranges::lazy_split_view v(b, ' '); + + auto i = (*v.begin()).begin(); + static_assert(std::same_as); + 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.lazy.split/range.lazy.split.inner/ctor.default.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.default.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/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-has-no-incomplete-ranges + +// constexpr inner-iterator::inner-iterator() = default; + +#include + +#include "../types.h" + +static_assert(std::is_default_constructible_v); +static_assert(std::is_default_constructible_v); + +constexpr bool test() { + { + [[maybe_unused]] InnerIterForward i; + } + + { + [[maybe_unused]] InnerIterInput i; + } + + 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.inner/ctor.outer_iterator.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/ctor.outer_iterator.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// constexpr explicit inner-iterator::inner-iterator(outer-iterator i); + +#include + +#include "../types.h" + +static_assert( std::is_constructible_v); +static_assert( std::is_constructible_v); +// Is only constructible if both the outer and the inner iterators have the same constness. +static_assert( std::is_constructible_v); +// Note: this works because of an implicit conversion (`OuterIterNonConst` is converted to `OuterIterConst`). +static_assert( std::is_constructible_v); +static_assert( std::is_constructible_v); +static_assert(!std::is_constructible_v); + +template +void CheckCopyInitialization(Inner); + +template +concept CannotCopyInitialize = + requires(Inner i) { CheckCopyInitialization(i); } && + !requires(Outer o) { CheckCopyInitialization(o); }; + +template +constexpr void test_impl() { + [[maybe_unused]] Inner i(Outer{}); + + // Verify that the constructor is `explicit`. + static_assert(CannotCopyInitialize); +} + +constexpr bool test() { + test_impl(); + test_impl(); + test_impl(); + test_impl(); + test_impl(); + + 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.inner/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/deref.pass.cpp @@ -0,0 +1,74 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr decltype(auto) inner-iterator::operator*() const; + +#include + +#include "../types.h" + +constexpr bool test() { + // Can call `inner-iterator::operator*`; `View` is a forward range. + { + SplitViewDiff v("abc def", " "); + auto val = *v.begin(); + + // Non-const iterator. + { + auto i = val.begin(); + static_assert(std::same_as); + assert(*i == 'a'); + assert(*(++i) == 'b'); + assert(*(++i) == 'c'); + } + + // Const iterator. + { + const auto ci = val.begin(); + static_assert(std::same_as); + assert(*ci == 'a'); + } + } + + // Can call `inner-iterator::operator*`; `View` is an input range. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + // Non-const iterator. + { + auto i = val.begin(); + static_assert(std::same_as); + assert(*i == 'a'); + assert(*(++i) == 'b'); + assert(*(++i) == 'c'); + } + + // Const iterator. + { + const auto ci = val.begin(); + static_assert(std::same_as); + // Note: this can be surprising. When the underlying range is an input range, `current` is stored in the + // `lazy_split_view` itself and shared between `inner-iterator`s. Consequently, incrementing one iterator + // effectively increments all of them. + assert(*ci == 'c'); + } + } + + 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.inner/equal.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/equal.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/equal.pass.cpp @@ -0,0 +1,76 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr bool operator==(const inner-iterator& x, const inner-iterator& y); +// requires forward_range; +// +// friend constexpr bool operator==(const inner-iterator& x, default_sentinel_t); + +#include + +#include +#include +#include "../types.h" + +template +concept CanCallEquals = requires(const Iter& i) { + i == i; + i != i; +}; + +constexpr bool test() { + // When `View` is a forward range, `inner-iterator` supports both overloads of `operator==`. + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + auto b = val.begin(); + std::same_as decltype(auto) e = val.end(); + + // inner-iterator == inner-iterator + { + assert(b == b); + assert(!(b != b)); + } + + // inner-iterator == default_sentinel + { + assert(!(b == e)); + assert(b != e); + + assert(!(b == std::default_sentinel)); + assert(b != std::default_sentinel); + } + } + + // When `View` is an input range, `inner-iterator only supports comparing an `inner-iterator` to the default sentinel. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + auto b = val.begin(); + std::same_as decltype(auto) e = val.end(); + + static_assert(!CanCallEquals); + + assert(!(b == std::default_sentinel)); + assert(b != std::default_sentinel); + assert(!(b == e)); + assert(b != e); + } + + 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.inner/increment.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/increment.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/increment.pass.cpp @@ -0,0 +1,127 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// constexpr inner-iterator& inner-iterator::operator++(); +// +// constexpr decltype(auto) inner-iterator::operator++(int); + +#include + +#include "../types.h" + +struct EmptyView : std::ranges::view_base { + constexpr int* begin() const { return nullptr; } + constexpr int* end() const { return nullptr; } + constexpr static size_t size() { return 0; } +}; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::view); +LIBCPP_STATIC_ASSERT(std::ranges::__tiny_range); + +constexpr bool test() { + // Can call `inner-iterator::operator++`; `View` is a forward range. + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + + // ++i + { + auto i = val.begin(); + assert(*i == 'a'); + + decltype(auto) i2 = ++i; + static_assert(std::is_lvalue_reference_v); + assert(*i2 == 'b'); + } + + // i++ + { + auto i = val.begin(); + assert(*i == 'a'); + + decltype(auto) i2 = i++; + static_assert(!std::is_reference_v); + assert(*i2 == 'a'); + assert(*i == 'b'); + } + } + + // Can call `inner-iterator::operator++`; `View` is an input range. + { + // ++i + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + auto i = val.begin(); + assert(*i == 'a'); + + decltype(auto) i2 = ++i; + static_assert(std::is_lvalue_reference_v); + assert(*i2 == 'b'); + } + + // i++ + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + auto i = val.begin(); + assert(*i == 'a'); + + static_assert(std::is_void_v); + i++; + assert(*i == 'b'); + } + } + + // Can call `inner-iterator::operator++`; `View` is an input range and `Pattern` is an "empty" range. + { + // ++i + { + std::ranges::lazy_split_view v("a", EmptyView()); + auto val = *v.begin(); + + auto i = val.begin(); + assert(*i.base() == 'a'); + assert(i != std::default_sentinel); + + // The iterator doesn't move to the next character but is considered to point to the end. + ++i; + assert(*i.base() == 'a'); + assert(i == std::default_sentinel); + } + + // i++ + { + std::ranges::lazy_split_view v("a", EmptyView()); + auto val = *v.begin(); + + auto i = val.begin(); + assert(*i.base() == 'a'); + assert(i != std::default_sentinel); + + // The iterator doesn't move to the next character but is considered to point to the end. + i++; + assert(*i.base() == 'a'); + assert(i == std::default_sentinel); + } + } + + 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.inner/iter_move.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_move.pass.cpp @@ -0,0 +1,158 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// friend constexpr decltype(auto) iter_move(const inner-iterator& i) +// noexcept(noexcept(ranges::iter_move(i.i_.))); + +#include + +#include +#include +#include +#include "../types.h" + +namespace adl { + +template +struct Iterator { + using value_type = int; + using difference_type = ptrdiff_t; + + value_type* ptr_ = nullptr; + int* iter_move_invocations_ = nullptr; + + constexpr Iterator() = default; + constexpr explicit Iterator(int* p, int& iter_moves) : ptr_(p), iter_move_invocations_(&iter_moves) {} + + constexpr value_type& operator*() const { return *ptr_; } + + Iterator& operator++() { ++ptr_; return *this; } + Iterator operator++(int) { + Iterator prev = *this; + ++ptr_; + return prev; + } + + constexpr Iterator& operator--() { --ptr_; return *this; } + constexpr Iterator operator--(int) { + Iterator prev = *this; + --ptr_; + return prev; + } + + constexpr friend value_type&& iter_move(Iterator iter) noexcept(IsNoexcept) { + if (iter.iter_move_invocations_) { + ++(*iter.iter_move_invocations_); + } + return std::move(*iter); + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } +}; + +template +struct View : std::ranges::view_base { + static constexpr int N = 3; + int a[N] = {0, 1, 2}; + int* iter_moves = nullptr; + + constexpr View() = default; + constexpr View(int& iter_move_invocations) : iter_moves(&iter_move_invocations) { + } + + constexpr adl::Iterator begin() { return adl::Iterator(a, *iter_moves); } + constexpr adl::Iterator end() { return adl::Iterator(a + N, *iter_moves); } +}; + +} // namespace adl + +constexpr bool test() { + // Can use `iter_move` with `inner-iterator`; `View` is a forward range. + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + + // Non-const iterator. + { + auto i = val.begin(); + static_assert(std::same_as); + assert(iter_move(i) == 'a'); + } + + // Const iterator. + { + const auto i = val.begin(); + static_assert(std::same_as); + assert(iter_move(i) == 'a'); + } + } + + // Can use `iter_move` with `inner-iterator`, `View` is an input range. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + // Non-const iterator. + { + auto i = val.begin(); + static_assert(std::same_as); + assert(iter_move(i) == 'a'); + } + + // Const iterator. + { + const auto i = val.begin(); + static_assert(std::same_as); + assert(iter_move(i) == 'a'); + } + } + + // Ensure the `iter_move` customization point is being used. + { + int iter_move_invocations = 0; + adl::View<> input(iter_move_invocations); + std::ranges::lazy_split_view, adl::View<>> v(input, adl::View<>()); + + auto val = *v.begin(); + auto i = val.begin(); + int x = iter_move(i); + assert(x == 0); + assert(iter_move_invocations == 1); + } + + // Check the `noexcept` specification. + { + { + using ThrowingSplitView = std::ranges::lazy_split_view, adl::View>; + using ThrowingValueType = std::ranges::iterator_t::value_type; + using ThrowingIter = std::ranges::iterator_t; + ASSERT_NOT_NOEXCEPT(std::ranges::iter_move(std::declval>())); + ASSERT_NOT_NOEXCEPT(iter_move(std::declval())); + } + + { + using NoexceptSplitView = std::ranges::lazy_split_view, adl::View>; + using NoexceptValueType = std::ranges::iterator_t::value_type; + using NoexceptIter = std::ranges::iterator_t; + ASSERT_NOEXCEPT(std::ranges::iter_move(std::declval>())); + ASSERT_NOEXCEPT(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.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/iter_swap.pass.cpp @@ -0,0 +1,217 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// friend constexpr void iter_swap(const inner-iterator& x, const inner-iterator& y) +// noexcept(noexcept(ranges::iter_swap(x.i_., y.i_.))) +// requires indirectly_swappable>; + +#include + +#include +#include +#include +#include "../types.h" + +#include + +namespace adl { + +template +struct Iterator { + using value_type = int; + using difference_type = ptrdiff_t; + + value_type* ptr_ = nullptr; + int* iter_swap_invocations_ = nullptr; + + constexpr Iterator() = default; + constexpr explicit Iterator(int& iter_swaps) : iter_swap_invocations_(&iter_swaps) {} + + value_type& operator*() const { return *ptr_; } + + Iterator& operator++() { ++ptr_; return *this; } + Iterator operator++(int) { + Iterator prev = *this; + ++ptr_; + return prev; + } + + Iterator& operator--() { --ptr_; return *this; } + Iterator operator--(int) { + Iterator prev = *this; + --ptr_; + return prev; + } + + constexpr friend void iter_swap(Iterator a, Iterator) noexcept(IsNoexcept) { + if (a.iter_swap_invocations_) { + ++(*a.iter_swap_invocations_); + } + } + + friend bool operator==(const Iterator& lhs, const Iterator& rhs) { return lhs.ptr_ == rhs.ptr_; } +}; + +template +struct View : std::ranges::view_base { + int* iter_swaps = nullptr; + + constexpr View() = default; + constexpr View(int& iter_swap_invocations) : iter_swaps(&iter_swap_invocations) { + } + + constexpr adl::Iterator begin() { return adl::Iterator(*iter_swaps); } + constexpr adl::Iterator end() { return adl::Iterator(*iter_swaps); } +}; + +} // namespace adl + +constexpr bool test() { + // Can use `iter_swap` with `inner-iterator`; `View` is a forward range. + { + // Non-const iterator. + { + SplitViewDiff v("abc def", " "); + auto val = *v.begin(); + + auto i1 = val.begin(); + auto i2 = i1++; + static_assert(std::is_void_v); + assert(*i1 == 'b'); + assert(*i2 == 'a'); + + iter_swap(i1, i2); + assert(*i1 == 'a'); + assert(*i2 == 'b'); + // Note that `iter_swap` swaps characters in the actual underlying range. + assert(*v.base().begin() == 'b'); + } + + // Const iterator. + { + SplitViewDiff v("abc def", " "); + auto val = *v.begin(); + + auto i1 = val.begin(); + const auto i2 = i1++; + static_assert(std::is_void_v); + static_assert(std::is_void_v); + assert(*i1 == 'b'); + assert(*i2 == 'a'); + + iter_swap(i1, i2); + assert(*i1 == 'a'); + assert(*i2 == 'b'); + assert(*v.base().begin() == 'b'); + } + } + + // Can use `iter_swap` with `inner-iterator`; `View` is an input range. + { + + // Non-const iterator. + { + // Iterators belong to the same view. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + auto i1 = val.begin(); + auto i2 = i1; + ++i1; + static_assert(std::is_void_v); + assert(*i1 == 'b'); + // For an input view, all inner iterators are essentially thin proxies to the same underlying iterator. + assert(*i2 == 'b'); + + iter_swap(i1, i2); + assert(*i1 == 'b'); + assert(*i2 == 'b'); + } + + // Iterators belong to different views. + { + SplitViewInput v1("abc def", ' '); + auto val1 = *v1.begin(); + SplitViewInput v2 = v1; + auto val2 = *v2.begin(); + + auto i1 = val1.begin(); + auto i2 = val2.begin(); + ++i1; + assert(*i1 == 'b'); + assert(*i2 == 'a'); + + iter_swap(i1, i2); + assert(*i1 == 'a'); + assert(*i2 == 'b'); + } + } + + // Const iterator. + { + SplitViewInput v("abc def", ' '); + auto val = *v.begin(); + + const auto i1 = val.begin(); + const auto i2 = i1; + static_assert(std::is_void_v); + assert(*i1 == 'a'); + assert(*i2 == 'a'); + + iter_swap(i1, i2); + assert(*i1 == 'a'); + assert(*i2 == 'a'); + } + } + + // Ensure the `iter_swap` customization point is being used. + { + int iter_swap_invocations = 0; + adl::View<> input(iter_swap_invocations); + std::ranges::lazy_split_view, adl::View<>> v(input, adl::View<>()); + + auto val = *v.begin(); + auto i = val.begin(); + iter_swap(i, i); + assert(iter_swap_invocations == 1); + } + + // Check the `noexcept` specification. + { + { + using ThrowingSplitView = std::ranges::lazy_split_view, adl::View>; + using ThrowingValueType = std::ranges::iterator_t::value_type; + using ThrowingIter = std::ranges::iterator_t; + ASSERT_NOT_NOEXCEPT( + std::ranges::iter_swap(std::declval>(), std::declval>())); + ASSERT_NOT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + } + + { + using NoexceptSplitView = std::ranges::lazy_split_view, adl::View>; + using NoexceptValueType = std::ranges::iterator_t::value_type; + using NoexceptIter = std::ranges::iterator_t; + ASSERT_NOEXCEPT( + std::ranges::iter_swap(std::declval>(), std::declval>())); + ASSERT_NOEXCEPT(iter_swap(std::declval(), std::declval())); + } + } + + 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.inner/types.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/types.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.inner/types.compile.pass.cpp @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// using iterator_category = If< +// derived_from>::iterator_category, forward_iterator_tag>, +// forward_iterator_tag, +// typename iterator_traits>::iterator_category +// >; +// using iterator_concept = typename outer-iterator::iterator_concept; +// using value_type = range_value_t; +// using difference_type = range_difference_t; + +#include + +#include +#include +#include "../types.h" + +template +using OuterIter = std::ranges::iterator_t>; +template +using InnerIter = std::ranges::iterator_t())>; + +// iterator_concept + +static_assert(std::same_as::iterator_concept, + typename OuterIter::iterator_concept>); +static_assert(std::same_as::iterator_concept, + typename OuterIter::iterator_concept>); + +// iterator_category + +static_assert(std::same_as::iterator_category, std::forward_iterator_tag>); + +template +concept NoIteratorCategory = !requires { typename InnerIter::iterator_category; }; +static_assert(NoIteratorCategory); + +// value_type + +static_assert(std::same_as::value_type, + std::ranges::range_value_t>); + +// difference_type + +static_assert(std::same_as::difference_type, + std::ranges::range_difference_t>); 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,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-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::begin() + +#include + +#include +#include "../types.h" + +constexpr bool operator==(const cpp20_input_iterator& lhs, const cpp20_input_iterator& rhs) { + return base(lhs) == base(rhs); +} + +constexpr bool test() { + // `View` is a forward range. + { + CopyableView input("a"); + + // Non-const. + { + SplitViewCopyable v(input, "b"); + auto val = *v.begin(); + assert(val.begin().base() == input.begin()); + } + + // Const. + { + SplitViewCopyable v(input, "b"); + const auto val = *v.begin(); + assert(val.begin().base() == input.begin()); + } + } + + // `View` is an input range. + { + InputView input("a"); + + // Non-const. + { + SplitViewInput v(input, 'b'); + auto val = *v.begin(); + // Copies of `InputView` are independent and the iterators won't compare the same. + assert(*val.begin().base() == *input.begin()); + } + + // Const. + { + SplitViewInput v(input, 'b'); + const auto val = *v.begin(); + // Copies of `InputView` are independent and the iterators won't compare the same. + assert(*val.begin().base() == *input.begin()); + } + } + + 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,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-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::value_type() + +#include + +#include "../types.h" + +static_assert(std::is_default_constructible_v); +static_assert(std::is_default_constructible_v); + +constexpr bool test() { + { + [[maybe_unused]] ValueTypeForward val; + } + + { + [[maybe_unused]] ValueTypeForward val = {}; + } + + { + [[maybe_unused]] ValueTypeInput val; + } + + { + [[maybe_unused]] ValueTypeInput val = {}; + } + + // Note: attempting a check like `val.begin() == val.end()` leads to null pointer dereference. + + 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.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,59 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// explicit outer-iterator::value_type::value_type(outer-iterator i) + +#include + +#include +#include "../types.h" + +static_assert(std::is_constructible_v); +static_assert(std::is_constructible_v); + +template +void CheckCopyInitialization(Inner); + +template +concept CannotCopyInitialize = + requires(Value v) { CheckCopyInitialization(v); } && + !requires(ConvertibleToValue c) { CheckCopyInitialization(c); }; + +// Verify that the constructor is `explicit`. +static_assert(CannotCopyInitialize); +static_assert(CannotCopyInitialize); + +constexpr bool test() { + // `View` is a forward range. + { + CopyableView input = "a"; + SplitViewCopyable v(input, "b"); + ValueTypeCopyable val(v.begin()); + assert(val.begin().base() == input.begin()); + } + + // `View` is an input range. + { + InputView input = "a"; + SplitViewInput v(input, 'b'); + ValueTypeInput val(v.begin()); + assert(*val.begin().base() == *input.begin()); + } + + 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.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,78 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::value_type::end() + +#include + +#include +#include "../types.h" + +constexpr bool test() { + // `View` is a forward range. + { + CopyableView input("a"); + + // Non-const. + { + SplitViewCopyable v(input, "b"); + auto val = *v.begin(); + + static_assert(std::same_as); + static_assert(noexcept(val.end())); + [[maybe_unused]] auto e = val.end(); + } + + // Const. + { + SplitViewCopyable v(input, "b"); + const auto val = *v.begin(); + + static_assert(std::same_as); + static_assert(noexcept(val.end())); + [[maybe_unused]] auto e = val.end(); + } + } + + // `View` is an input range. + { + InputView input("a"); + + // Non-const. + { + SplitViewInput v(input, 'b'); + auto val = *v.begin(); + + static_assert(std::same_as); + static_assert(noexcept(val.end())); + [[maybe_unused]] auto e = val.end(); + } + + // Const. + { + SplitViewInput v(input, 'b'); + const auto val = *v.begin(); + + static_assert(std::same_as); + static_assert(noexcept(val.end())); + [[maybe_unused]] auto e = val.end(); + } + } + + 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/view_interface.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/view_interface.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer.value/view_interface.pass.cpp @@ -0,0 +1,109 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view::outer-iterator::value_type; + +#include + +#include +#include +#include "../types.h" + +using V = ValueTypeForward; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::view); + +// empty() +template +concept CanCallEmpty = requires (View& v) { v.empty(); }; +static_assert(CanCallEmpty); + +// operator bool() +template +concept CanCallOperatorBool = requires (View& v) { static_cast(v); }; +static_assert(CanCallOperatorBool); + +// front() +template +concept CanCallFront = requires (View& v) { v.front(); }; +static_assert(CanCallFront); + +// size() is unavailable. +static_assert(!std::sized_sentinel_for, std::ranges::iterator_t>); +template +concept CanCallSize = requires (View& v) { v.size(); }; +static_assert(!CanCallSize); + +// back() is unavailable. +static_assert(!std::ranges::bidirectional_range); +template +concept CanCallBack = requires (View& v) { v.back(); }; +static_assert(!CanCallBack); + +// data() is unavailable. +static_assert(!std::contiguous_iterator>); +template +concept CanCallData = requires (View& v) { v.data(); }; +static_assert(!CanCallData); + +// operator[] is unavailable. +static_assert(!std::ranges::random_access_range); +template +concept CanCallOperatorSubscript = requires (View& v) { v[0]; }; +static_assert(!CanCallOperatorSubscript); + +constexpr bool test() { + // empty() + { + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + assert(!val.empty()); + } + + { + SplitViewForward v; + auto val = *v.begin(); + assert(val.empty()); + } + } + + // operator bool() + { + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + assert(val); + } + + { + SplitViewForward v; + auto val = *v.begin(); + assert(!val); + } + } + + // front() + { + SplitViewForward v("abc def", " "); + auto val = *v.begin(); + assert(val.front() == 'a'); + } + + 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/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,55 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// constexpr outer-iterator(outer-iterator i) +// requires Const && convertible_to, iterator_t> + +#include + +#include +#include +#include +#include "../types.h" + +// outer-iterator + +template +concept IsConstOuterIter = requires (Iter i) { + { *(*i).begin() } -> std::same_as; +}; +static_assert( IsConstOuterIter); + +static_assert( std::convertible_to< + std::ranges::iterator_t, std::ranges::iterator_t>); + +// outer-iterator + +template +concept IsNonConstOuterIter = requires (Iter i) { + { *(*i).begin() } -> std::same_as; +}; +static_assert( IsNonConstOuterIter); + +static_assert( std::is_constructible_v); +static_assert(!std::is_constructible_v); + +constexpr bool test() { + [[maybe_unused]] OuterIterConst i(OuterIterNonConst{}); + + 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/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,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-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::outer-iterator() + +#include + +#include "../types.h" + +static_assert(std::is_default_constructible_v); +static_assert(std::is_default_constructible_v); + +constexpr bool test() { + // `View` is a forward range. + { + [[maybe_unused]] OuterIterForward i; + } + + { + [[maybe_unused]] OuterIterForward i = {}; + } + + // `View` is an input range. + { + [[maybe_unused]] OuterIterInput i; + } + + { + [[maybe_unused]] OuterIterInput i = {}; + } + + 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/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,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-has-no-incomplete-ranges + +// std::ranges::lazy_split_view::outer-iterator::outer-iterator(Parent& parent) +// requires (!forward_range) + +#include + +#include +#include +#include "../types.h" + +static_assert( std::ranges::forward_range); +static_assert(!std::is_constructible_v); + +static_assert( std::ranges::input_range); +static_assert( std::is_constructible_v); + +constexpr bool test() { + InputView input; + SplitViewInput v(input, ForwardTinyView()); + [[maybe_unused]] OuterIterInput i(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/ctor.parent_base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/ctor.parent_base.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-has-no-incomplete-ranges + +// constexpr outer-iterator(Parent& parent, iterator_t current); +// requires forward_range + +#include + +#include +#include +#include "../types.h" + +static_assert( std::ranges::forward_range); +static_assert( std::is_constructible_v>); + +static_assert(!std::ranges::forward_range); +static_assert(!std::is_constructible_v>); + +constexpr bool test() { + ForwardView input("abc"); + SplitViewForward v(std::move(input), " "); + [[maybe_unused]] OuterIterForward i(v, input.begin()); + + 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/deref.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/deref.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/range.lazy.split.outer/deref.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-has-no-incomplete-ranges + +// constexpr outer-iterator::value-type outer-iterator::operator*() const; + +#include + +#include +#include +#include "../small_string.h" +#include "../types.h" + +constexpr bool test() { + // `View` is a forward range. + { + SplitViewDiff v("abc def ghi", " "); + + // Non-const iterator. + { + auto i = v.begin(); + static_assert(!std::is_reference_v); + assert(SmallString(*i) == "abc"_str); + assert(SmallString(*(++i)) == "def"_str); + assert(SmallString(*(++i)) == "ghi"_str); + } + + // Const iterator. + { + const auto ci = v.begin(); + static_assert(!std::is_reference_v); + assert(SmallString(*ci) == "abc"_str); + } + } + + // `View` is an input range. + { + SplitViewInput v("abc def ghi", ' '); + + // Non-const iterator. + { + auto i = v.begin(); + static_assert(!std::is_reference_v); + assert(SmallString(*i) == "abc"_str); + assert(SmallString(*(++i)) == "def"_str); + assert(SmallString(*(++i)) == "ghi"_str); + } + + // Const iterator. + { + const auto ci = v.begin(); + static_assert(!std::is_reference_v); + assert(SmallString(*ci) == "abc"_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/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,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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// friend constexpr bool operator==(const outer-iterator& x, const outer-iterator& y) +// requires forward_range; +// +// friend constexpr bool operator==(const outer-iterator& x, default_sentinel_t); + +#include + +#include +#include +#include "../types.h" + +template +concept CanCallEquals = requires(const Iter& i) { + i == i; + i != i; +}; + +constexpr bool test() { + // Forward range supports both overloads of `operator==`. + { + // outer-iterator == outer-iterator + { + SplitViewForward v("abc def", " "); + auto b = v.begin(), e = v.end(); + + assert(b == b); + assert(!(b != b)); + + assert(e == e); + assert(!(e != e)); + + assert(!(b == e)); + assert(b != e); + } + + // outer-iterator == default_sentinel + { + SplitViewForward v("abc def", " "); + auto b = v.begin(), e = v.end(); + + assert(!(b == std::default_sentinel)); + assert(b != std::default_sentinel); + assert(e == std::default_sentinel); + assert(!(e != std::default_sentinel)); + } + + // Default-constructed `outer-iterator`s compare equal. + { + OuterIterForward i1, i2; + assert(i1 == i2); + assert(!(i1 != i2)); + } + } + + // Input range only supports comparing an `outer-iterator` to the default sentinel. + { + using namespace std::string_view_literals; + SplitViewInput v("abc def"sv, ' '); + auto b = v.begin(); + std::same_as decltype(auto) e = v.end(); + + static_assert(!CanCallEquals); + + assert(!(b == std::default_sentinel)); + assert(b != std::default_sentinel); + assert(!(b == e)); + assert(b != e); + } + + 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/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,82 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// constexpr outer-iterator& outer-iterator::operator++(); +// constexpr decltype(auto) outer-iterator::operator++(int); + +#include + +#include +#include +#include "../small_string.h" +#include "../types.h" + +constexpr bool test() { + // Can call `outer-iterator::operator++`; `View` is a forward range. + { + SplitViewForward v("abc def ghi", " "); + + // ++i + { + auto i = v.begin(); + assert(*i == "abc"_str); + + decltype(auto) i2 = ++i; + static_assert(std::is_lvalue_reference_v); + assert(*i2 == "def"_str); + } + + // i++ + { + auto i = v.begin(); + assert(*i == "abc"_str); + + decltype(auto) i2 = i++; + static_assert(!std::is_reference_v); + assert(*i2 == "abc"_str); + assert(*i == "def"_str); + } + } + + // Can call `outer-iterator::operator++`; `View` is an input range. + { + SplitViewInput v("abc def ghi", ' '); + + // ++i + { + auto i = v.begin(); + assert(*i == "abc"_str); + + decltype(auto) i2 = ++i; + static_assert(std::is_lvalue_reference_v); + assert(*i2 == "def"_str); + } + + // i++ + { + auto i = v.begin(); + assert(*i == "abc"_str); + + static_assert(std::is_void_v); + i++; + assert(*i == "def"_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/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,41 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// UNSUPPORTED: libcpp-has-no-incomplete-ranges + +// using iterator_category = input_iterator_tag; // Only defined if `View` is a forward range. +// using iterator_concept = conditional_t, forward_iterator_tag, input_iterator_tag>; +// using difference_type = range_difference_t; + +#include + +#include +#include +#include "../types.h" + +template +using OuterIter = decltype(std::declval>().begin()); + +// iterator_category + +static_assert(std::same_as::iterator_category, std::input_iterator_tag>); + +template +concept NoIteratorCategory = !requires { typename OuterIter::iterator_category; }; +static_assert(NoIteratorCategory); + +// iterator_concept + +static_assert(std::same_as::iterator_concept, std::forward_iterator_tag>); +static_assert(std::same_as::iterator_concept, std::input_iterator_tag>); + +// difference_type + +static_assert(std::same_as::difference_type, + std::ranges::range_difference_t>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.lazy.split/small_string.h b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/small_string.h new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.lazy.split/small_string.h @@ -0,0 +1,88 @@ +//===----------------------------------------------------------------------===// +// +// 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_SMALL_STRING_H +#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_SMALL_STRING_H + +#include +#include +#include +#include + +// A constexpr-friendly lightweight string, primarily useful for comparisons. +// Unlike `std::string`, all functions are `constexpr`. Unlike `std::string_view`, it copies the given string into an +// internal buffer and can work with non-contiguous inputs. +// +// TODO(var-const): remove once https://reviews.llvm.org/D110598 lands and `std::string` can be used instead of this +// class. +template +class BasicSmallString { + constexpr static int N = 32; + Char buffer_[N] = {}; + size_t size_ = 0; + + constexpr void omit_terminating_null() { + if (size_ > 0 && buffer_[size_ - 1] == '\0') { + --size_; + } + } + +public: + // Main constructors. + + constexpr BasicSmallString() = default; + + constexpr BasicSmallString(std::basic_string_view v) : size_(v.size()) { + assert(size_ < N); + if (size_ == 0) return; + + std::copy(v.begin(), v.end(), buffer_); + omit_terminating_null(); + } + + template + constexpr BasicSmallString(I b, const S& e) { + for (; b != e; ++b) { + buffer_[size_++] = *b; + assert(size_ < N); + } + + omit_terminating_null(); + } + + // Delegating constructors. + + constexpr BasicSmallString(const Char* ptr, size_t size) : BasicSmallString(std::basic_string_view(ptr, size)) { + } + + template + constexpr BasicSmallString(R&& from) : BasicSmallString(from.begin(), from.end()) { + } + + // Iterators. + + constexpr Char* begin() { return buffer_; } + constexpr Char* end() { return buffer_ + size_; } + constexpr const Char* begin() const { return buffer_; } + constexpr const Char* end() const { return buffer_ + size_; } + + friend constexpr bool operator==(const BasicSmallString& lhs, const BasicSmallString& rhs) { + return lhs.size_ == rhs.size_ && std::equal(lhs.buffer_, lhs.buffer_ + lhs.size_, rhs.buffer_); + } + friend constexpr bool operator==(const BasicSmallString& lhs, std::string_view rhs) { + return lhs == BasicSmallString(rhs); + } +}; + +using SmallString = BasicSmallString; + +constexpr SmallString operator "" _str(const char* ptr, size_t size) { + return SmallString(ptr, size); +} + +#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_LAZY_SPLIT_SMALL_STRING_H 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,202 @@ +//===----------------------------------------------------------------------===// +// +// 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 +#include +#include +#include +#include "small_string.h" +#include "test_macros.h" +#include "test_iterators.h" + +// CopyableView + +struct CopyableView : std::ranges::view_base { + std::string_view view_; + constexpr explicit CopyableView() = default; + constexpr CopyableView(const char* ptr) : view_(ptr) {} + constexpr CopyableView(std::string_view v) : view_(v) {} + constexpr forward_iterator begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } + constexpr bool operator==(const CopyableView& rhs) const { return view_ == rhs.view_; } +}; +static_assert( std::ranges::forward_range); +static_assert( std::ranges::forward_range); +static_assert( std::ranges::view); +static_assert( std::is_copy_constructible_v); + +// ForwardView + +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 begin() const { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(view_.end()); } +}; +static_assert( std::ranges::forward_range); +static_assert( std::ranges::forward_range); +static_assert( std::ranges::view); +static_assert(!std::is_copy_constructible_v); +static_assert( std::is_move_constructible_v); + +// ForwardDiffView + +// Iterator types differ based on constness of this class. +struct ForwardDiffView : std::ranges::view_base { + SmallString buffer_; + constexpr explicit ForwardDiffView() = default; + constexpr ForwardDiffView(const char* ptr) : ForwardDiffView(std::string_view(ptr)) {} + constexpr ForwardDiffView(std::string_view v) : buffer_(v) {} + constexpr ForwardDiffView(ForwardDiffView&&) = default; + constexpr ForwardDiffView& operator=(ForwardDiffView&&) = default; + constexpr ForwardDiffView(const ForwardDiffView&) = default; + constexpr ForwardDiffView& operator=(const ForwardDiffView&) = default; + constexpr forward_iterator begin() { return forward_iterator(buffer_.begin()); } + constexpr forward_iterator end() { return forward_iterator(buffer_.end()); } + constexpr forward_iterator begin() const { return forward_iterator(buffer_.begin()); } + constexpr forward_iterator end() const { return forward_iterator(buffer_.end()); } +}; +static_assert( std::ranges::forward_range); +static_assert( std::ranges::forward_range); +static_assert( std::ranges::view); +static_assert(!std::same_as, std::ranges::iterator_t>); + +// ForwardOnlyIfNonConstView + +template +class almost_forward_iterator { + It it_; + + template friend class almost_forward_iterator; + +public: + using iterator_category = std::forward_iterator_tag; + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + using pointer = It; + using reference = typename std::iterator_traits::reference; + + constexpr almost_forward_iterator() : it_() {} + constexpr explicit almost_forward_iterator(It it) : it_(it) {} + template + constexpr almost_forward_iterator(const almost_forward_iterator& u) : it_(u.it_) {} + + constexpr reference operator*() const { return *it_; } + constexpr pointer operator->() const { return it_; } + + constexpr almost_forward_iterator& operator++() { ++it_; return *this; } + // Notice the slightly different return type. + constexpr const almost_forward_iterator operator++(int) { return almost_forward_iterator(it_); } + + friend constexpr bool operator==(const almost_forward_iterator& x, const almost_forward_iterator& y) { + return x.it_ == y.it_; + } + friend constexpr bool operator!=(const almost_forward_iterator& x, const almost_forward_iterator& y) { + return x.it_ != y.it_; + } +}; +static_assert(!std::forward_iterator>); +static_assert( std::input_iterator>); + +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 begin() { return forward_iterator(view_.begin()); } + constexpr forward_iterator end() { return forward_iterator(view_.end()); } + constexpr almost_forward_iterator begin() const { + return almost_forward_iterator(view_.begin()); + } + constexpr almost_forward_iterator end() const { + return almost_forward_iterator(view_.end()); + } +}; +static_assert( std::ranges::forward_range); +static_assert(!std::ranges::forward_range); +static_assert( std::ranges::view); + +// InputView + +struct InputView : std::ranges::view_base { + SmallString buffer_; + + constexpr InputView() = default; + constexpr InputView(const char* s) : InputView(std::string_view(s)) {} + constexpr InputView(std::string_view v) : buffer_(v) {} + + constexpr cpp20_input_iterator begin() { return cpp20_input_iterator(buffer_.begin()); } + constexpr sentinel_wrapper> end() { + return sentinel_wrapper(cpp20_input_iterator(buffer_.end())); + } + constexpr cpp20_input_iterator begin() const { + return cpp20_input_iterator(buffer_.begin()); + } + constexpr sentinel_wrapper> end() const { + return sentinel_wrapper(cpp20_input_iterator(buffer_.end())); + } +}; + +static_assert(std::ranges::input_range); +static_assert(std::ranges::input_range); +static_assert(std::ranges::view); + +// ForwardTinyView + +struct ForwardTinyView : std::ranges::view_base { + char c_[1] = {}; + constexpr ForwardTinyView() = default; + constexpr ForwardTinyView(char c) { *c_ = c; } + constexpr forward_iterator begin() const { return forward_iterator(c_); } + constexpr forward_iterator end() const { return forward_iterator(c_ + 1); } + constexpr static size_t size() { return 1; } +}; +static_assert(std::ranges::forward_range); +static_assert(std::ranges::view); +LIBCPP_STATIC_ASSERT(std::ranges::__tiny_range); + +// Aliases + +using SplitViewCopyable = std::ranges::lazy_split_view; +using OuterIterCopyable = std::ranges::iterator_t; +using ValueTypeCopyable = OuterIterCopyable::value_type; +using InnerIterCopyable = std::ranges::iterator_t; +using BaseIterCopyable = std::ranges::iterator_t; + +using SplitViewForward = std::ranges::lazy_split_view; +using OuterIterForward = std::ranges::iterator_t; +using ValueTypeForward = OuterIterForward::value_type; +using InnerIterForward = std::ranges::iterator_t; +using BaseIterForward = std::ranges::iterator_t; + +using SplitViewInput = std::ranges::lazy_split_view; +using OuterIterInput = std::ranges::iterator_t; +using ValueTypeInput = OuterIterInput::value_type; +using InnerIterInput = std::ranges::iterator_t; +using BaseIterInput = std::ranges::iterator_t; + +using SplitViewDiff = std::ranges::lazy_split_view; +using OuterIterConst = decltype(std::declval().begin()); +using OuterIterNonConst = decltype(std::declval().begin()); +static_assert(!std::same_as); +using InnerIterConst = decltype((*std::declval()).begin()); +using InnerIterNonConst = decltype((*std::declval()).begin()); +static_assert(!std::same_as); + +#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,110 @@ +//===----------------------------------------------------------------------===// +// +// 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-has-no-incomplete-ranges + +// class std::ranges::lazy_split_view; + +#include + +#include +#include +#include +#include "types.h" + +using V = SplitViewForward; +static_assert(std::ranges::forward_range); + +// empty() +template +concept CanCallEmpty = requires (View& v) { v.empty(); }; +static_assert(CanCallEmpty); + +// operator bool() +template +concept CanCallOperatorBool = requires (View& v) { static_cast(v); }; +static_assert(CanCallOperatorBool); + +// front() +template +concept CanCallFront = requires (View& v) { v.front(); }; +static_assert(CanCallFront); + +// size() is unavailable. +static_assert(!std::sized_sentinel_for, std::ranges::iterator_t>); +template +concept CanCallSize = requires (View& v) { v.size(); }; +static_assert(!CanCallSize); + +// back() is unavailable. +static_assert(!std::ranges::bidirectional_range); +template +concept CanCallBack = requires (View& v) { v.back(); }; +static_assert(!CanCallBack); + +// data() is unavailable. +static_assert(!std::contiguous_iterator>); +template +concept CanCallData = requires (View& v) { v.data(); }; +static_assert(!CanCallData); + +// operator[] is unavailable. +static_assert(!std::ranges::random_access_range); +template +concept CanCallOperatorSubscript = requires (View& v) { v[0]; }; +static_assert(!CanCallOperatorSubscript); + +constexpr bool test() { + using namespace std::string_view_literals; + + // empty() + { + { + std::ranges::lazy_split_view v("abc def", " "); + assert(!v.empty()); + } + + { + // Note: an empty string literal would still produce a non-empty output because the terminating zero is treated as + // a separate character; hence the use of `string_view`. + std::ranges::lazy_split_view v(""sv, ""); + assert(v.empty()); + } + } + + // operator bool() + { + { + std::ranges::lazy_split_view v("abc", ""); + assert(v); + } + + { + // Note: an empty string literal would still produce a non-empty output because the terminating zero is treated as + // a separate character; hence the use of `string_view`. + std::ranges::lazy_split_view v(""sv, ""); + assert(!v); + } + } + + // front() + { + SplitViewForward v("abc", ""); + assert(*(v.front()).begin() == 'a'); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + 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 @@ -506,6 +506,7 @@ template void operator,(T const &) = delete; }; +static_assert(std::input_iterator>); template struct iter_value_or_void { using type = void; };