diff --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst --- a/libcxx/docs/ReleaseNotes.rst +++ b/libcxx/docs/ReleaseNotes.rst @@ -46,6 +46,7 @@ ``from_chars`` for Integral Types in ```` Header - P0220R1 - Adopt Library Fundamentals V1 TS Components for C++17 - P0482R6 - char8_t: A type for UTF-8 characters and strings +- P2446R2 - ``views::as_rvalue`` Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv --- a/libcxx/docs/Status/Cxx2bPapers.csv +++ b/libcxx/docs/Status/Cxx2bPapers.csv @@ -73,7 +73,7 @@ "`P2419R2 `__","LWG","Clarify handling of encodings in localized formatting of chrono types","July 2022","","" "`P2438R2 `__","LWG","``std::string::substr() &&``","July 2022","","" "`P2445R1 `__","LWG","``forward_like``","July 2022","|Complete|","16.0" -"`P2446R2 `__","LWG","``views::as_rvalue``","July 2022","","" +"`P2446R2 `__","LWG","``views::as_rvalue``","July 2022","|Complete|","16.0" "`P2460R2 `__","LWG","Relax requirements on ``wchar_t`` to match existing practices","July 2022","","" "`P2465R3 `__","LWG","Standard Library Modules ``std`` and ``std.compat``","July 2022","","" "`P2467R1 `__","LWG","Support exclusive mode for ``fstreams``","July 2022","","" diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -477,6 +477,7 @@ __random/weibull_distribution.h __ranges/access.h __ranges/all.h + __ranges/as_rvalue_view.h __ranges/common_view.h __ranges/concepts.h __ranges/copyable_box.h diff --git a/libcxx/include/__iterator/move_sentinel.h b/libcxx/include/__iterator/move_sentinel.h --- a/libcxx/include/__iterator/move_sentinel.h +++ b/libcxx/include/__iterator/move_sentinel.h @@ -50,6 +50,8 @@ _Sent __last_ = _Sent(); }; +_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(move_sentinel); + #endif // _LIBCPP_STD_VER > 17 _LIBCPP_END_NAMESPACE_STD diff --git a/libcxx/include/__ranges/as_rvalue_view.h b/libcxx/include/__ranges/as_rvalue_view.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__ranges/as_rvalue_view.h @@ -0,0 +1,129 @@ +//===----------------------------------------------------------------------===// +// +// 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_AS_RVALUE_H +#define _LIBCPP___RANGES_AS_RVALUE_H + +#include <__config> +#include <__iterator/move_iterator.h> +#include <__iterator/move_sentinel.h> +#include <__ranges/all.h> +#include <__ranges/concepts.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +#if _LIBCPP_STD_VER >= 23 + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { +template + requires input_range<_View> +class as_rvalue_view : public view_interface> { + _LIBCPP_NO_UNIQUE_ADDRESS _View __base_ = _View(); + +public: + _LIBCPP_HIDE_FROM_ABI as_rvalue_view() + requires default_initializable<_View> + = default; + + _LIBCPP_HIDE_FROM_ABI constexpr explicit as_rvalue_view(_View __base) : __base_(std::move(__base)) {} + + _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() + requires(!__simple_view<_View>) + { + return move_iterator(ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto begin() const + requires range + { + return move_iterator(ranges::begin(__base_)); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() + requires(!__simple_view<_View>) + { + if constexpr (common_range<_View>) { + return move_iterator(ranges::end(__base_)); + } else { + return move_sentinel(ranges::end(__base_)); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto end() const + requires range + { + if constexpr (common_range) { + return move_iterator(ranges::end(__base_)); + } else { + return move_sentinel(ranges::end(__base_)); + } + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() + requires sized_range<_View> + { + return ranges::size(__base_); + } + + _LIBCPP_HIDE_FROM_ABI constexpr auto size() const + requires sized_range + { + return ranges::size(__base_); + } +}; + +template +as_rvalue_view(_Range&&) -> as_rvalue_view>; + +template +inline constexpr bool enable_borrowed_range> = enable_borrowed_range<_View>; + +namespace views { +namespace __as_rvalue { +struct __fn : __range_adaptor_closure<__fn> { + template + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const + noexcept(noexcept(/**/ as_rvalue_view(std::forward<_Range>(__range)))) + -> decltype(/*--*/ as_rvalue_view(std::forward<_Range>(__range))) { + return /*-------------*/ as_rvalue_view(std::forward<_Range>(__range)); + } + + template + requires same_as, range_reference_t<_Range>> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Range&& __range) const + noexcept(noexcept(/**/ views::all(std::forward<_Range>(__range)))) + -> decltype(/*--*/ views::all(std::forward<_Range>(__range))) { + return /*-------------*/ views::all(std::forward<_Range>(__range)); + } +}; +} // namespace __as_rvalue + +inline namespace __cpo { +constexpr auto as_rvalue = __as_rvalue::__fn{}; +} // namespace __cpo +} // namespace views +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER >= 23 + +#endif // _LIBCPP___RANGES_AS_RVALUE_H diff --git a/libcxx/include/ranges b/libcxx/include/ranges --- a/libcxx/include/ranges +++ b/libcxx/include/ranges @@ -309,6 +309,7 @@ #include <__config> #include <__ranges/access.h> #include <__ranges/all.h> +#include <__ranges/as_rvalue_view.h> #include <__ranges/common_view.h> #include <__ranges/concepts.h> #include <__ranges/counted.h> diff --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp --- a/libcxx/test/libcxx/private_headers.verify.cpp +++ b/libcxx/test/libcxx/private_headers.verify.cpp @@ -508,6 +508,7 @@ #include <__random/weibull_distribution.h> // expected-error@*:* {{use of private header from outside its module: '__random/weibull_distribution.h'}} #include <__ranges/access.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/access.h'}} #include <__ranges/all.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/all.h'}} +#include <__ranges/as_rvalue_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/as_rvalue_view.h'}} #include <__ranges/common_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/common_view.h'}} #include <__ranges/concepts.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/concepts.h'}} #include <__ranges/copyable_box.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/copyable_box.h'}} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/adaptor.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/adaptor.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/adaptor.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, c++20 + +// std::views::as_rvalue + +#include +#include + +#include "test_iterators.h" + +struct View : std::ranges::view_base { + int i; + int* begin(); + int* end(); +}; + +static_assert(std::random_access_iterator>); + +struct RValueView : std::ranges::view_base {}; + +template +concept HasPipe = requires(View&& view, T&& t) { + { std::forward(view) | std::forward(t) }; + }; + +struct NoView {}; +static_assert(std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(!std::is_invocable_v); +static_assert(HasPipe); +static_assert(HasPipe); +static_assert(!HasPipe); +static_assert(!HasPipe); +static_assert(std::is_same_v); + +constexpr bool test() { + { // view | views::as_rvalue + View v{{}, 3}; + std::same_as> auto view = v | std::views::as_rvalue; + assert(view.base().i == 3); + } + { // adaptor | views::as_rvalue + View v{{}, 3}; + const auto partial = std::views::transform(std::identity{}) | std::views::as_rvalue; + std::same_as>> auto view = partial(v); + assert(view.base().base().i == 3); + } + { // adaptor | views::as_rvalue + View v{{}, 3}; + const auto partial = std::views::as_rvalue | std::views::transform(std::identity{}); + std::same_as, std::identity>> auto view = partial(v); + assert(view.base().base().i == 3); + } + { // rvalue_range | views::as_rvalue + int a[4] = {1, 2, 3, 4}; + std::ranges::subrange range(rvalue_iterator(a), rvalue_iterator(a + 4)); + [[maybe_unused]] std::same_as>> auto rval_range = + range | std::views::as_rvalue; + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/base.pass.cpp @@ -0,0 +1,77 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr V base() const & requires copy_­constructible { return base_; } +// constexpr V base() && { return std::move(base_); } + +#include +#include + +#include "MoveOnly.h" + +struct View : std::ranges::view_base { + int i; + int* begin() const; + int* end() const; +}; + +struct MoveOnlyView : View { + MoveOnly m; +}; + +template +concept HasBase = requires(T&& t) { std::forward(t).base(); }; + +static_assert(HasBase const&>); +static_assert(HasBase&&>); + +static_assert(!HasBase const&>); +static_assert(HasBase&&>); + +constexpr bool test() { + { // const & + const std::ranges::as_rvalue_view view(View{{}, 5}); + std::same_as decltype(auto) v = view.base(); + assert(v.i == 5); + } + + { // & + std::ranges::as_rvalue_view view(View{{}, 5}); + std::same_as decltype(auto) v = view.base(); + assert(v.i == 5); + } + + { // && + std::ranges::as_rvalue_view view(View{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.i == 5); + } + + { // const && + const std::ranges::as_rvalue_view view(View{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.i == 5); + } + + { // move only + std::ranges::as_rvalue_view view(MoveOnlyView{{}, 5}); + std::same_as decltype(auto) v = std::move(view).base(); + assert(v.m.get() == 5); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/begin.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/begin.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/begin.pass.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// constexpr auto begin() +// constexpr auto begin() const + +#include +#include +#include + +#include "test_iterators.h" + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template +concept HasBegin = requires(T t) { t.begin(); }; + +static_assert(HasBegin>); +static_assert(HasBegin>); + +template +constexpr void test_range() { + int a[] = {1, 2}; + std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a)))); + std::ranges::as_rvalue_view view(std::move(range)); + std::same_as> decltype(auto) iter = view.begin(); + assert(base(iter.base()) == std::begin(a)); +} + +template +constexpr void test_range_sentinels() { + test_range(); + test_range>(); + test_range>(); +} + +constexpr bool test() { + test_range, sentinel_wrapper>>(); + test_range, sized_sentinel>>(); + test_range, sentinel_wrapper>>(); + test_range, sized_sentinel>>(); + test_range_sentinels>(); + test_range_sentinels>(); + test_range_sentinels>(); + test_range_sentinels>(); + test_range_sentinels(); + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctad.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctad.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/ctad.compile.pass.cpp @@ -0,0 +1,22 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include + +static_assert(std::is_same_v{})), + std::ranges::as_rvalue_view>>>); + +static_assert(std::is_same_v&>())), + std::ranges::as_rvalue_view&>>>); + +static_assert(std::is_same_v{})), + std::ranges::as_rvalue_view>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/enable_borrowed_range.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/enable_borrowed_range.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/enable_borrowed_range.compile.pass.cpp @@ -0,0 +1,16 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include + +static_assert(std::ranges::enable_borrowed_range>>); +static_assert(std::ranges::enable_borrowed_range&>>>); +static_assert(!std::ranges::enable_borrowed_range>>>); diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/end.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, c++20 + +// constexpr auto end() +// constexpr auto end() const + +#include +#include +#include + +#include "test_iterators.h" + +struct View : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +struct CVCallView : std::ranges::view_base { + mutable bool const_called = false; + mutable int i[1]; + constexpr int* begin() { + const_called = false; + return i; + } + + constexpr int* begin() const { + const_called = true; + return i; + } + + constexpr int* end() { + const_called = false; + return i + 1; + } + + constexpr int* end() const { + const_called = true; + return i + 1; + } +}; + +struct NonConstCommonRange : std::ranges::view_base { + int* begin(); + int* end(); + + int* begin() const; + sentinel_wrapper end() const; +}; + +template +concept HasBegin = requires(T t) { t.end(); }; + +static_assert(HasBegin>); +static_assert(HasBegin>); + +static_assert( + std::is_same_v>().end()), std::move_iterator>); +static_assert(std::is_same_v>().end()), + std::move_sentinel>>); + +template +constexpr void test_range() { + using Expected = std::conditional_t, std::move_sentinel>; + int a[] = {1, 2}; + std::ranges::subrange range(Iter(std::begin(a)), Sent(Iter(std::end(a)))); + std::ranges::as_rvalue_view view(std::move(range)); + std::same_as decltype(auto) iter = view.end(); + assert(base(base(iter.base())) == std::end(a)); +} + +template +constexpr void test_range_sentinels() { + test_range(); + test_range, false>(); + test_range, false>(); +} + +constexpr bool test() { + test_range, sentinel_wrapper>, false>(); + test_range, sized_sentinel>, false>(); + test_range, sentinel_wrapper>, false>(); + test_range, sized_sentinel>, false>(); + test_range_sentinels>(); + test_range_sentinels>(); + test_range_sentinels>(); + test_range_sentinels>(); + test_range_sentinels(); + + { + std::ranges::as_rvalue_view view(CVCallView{}); + (void)view.begin(); + assert(view.base().const_called); + } + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/size.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/size.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/ranges/range.adaptors/range.as.rvalue/size.pass.cpp @@ -0,0 +1,71 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// constexpr auto size() +// constexpr auto size() const + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +#include +#include +#include + +struct ConstSizedView : std::ranges::view_base { + bool* size_called; + int* begin() const; + int* end() const; + + constexpr size_t size() const { + *size_called = true; + return 3; + } +}; + +struct SizedView : std::ranges::view_base { + int* begin() const; + int* end() const; + + int size(); +}; + +struct UnsizedView : std::ranges::view_base { + int* begin() const; + int* end() const; +}; + +template +concept HasSize = requires(T v) { v.size(); }; + +static_assert(HasSize); +static_assert(HasSize); +static_assert(HasSize); +static_assert(!HasSize); +static_assert(!HasSize); +static_assert(!HasSize); + +constexpr bool test() { + { + bool size_called = false; + std::ranges::as_rvalue_view view(ConstSizedView{{}, &size_called}); + std::same_as auto size = view.size(); + assert(size == 3); + } + { + std::ranges::as_rvalue_view view(SizedView({})); + static_assert(std::is_same_v); + } + + 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 @@ -977,6 +977,79 @@ } // namespace adl +template +class rvalue_iterator { +public: + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + using difference_type = std::ptrdiff_t; + using reference = T&&; + using value_type = T; + + rvalue_iterator() = default; + constexpr rvalue_iterator(T* it) : it_(it) {} + + reference operator*() const { return std::move(*it_); } + + rvalue_iterator& operator++() { + ++it_; + return *this; + } + + rvalue_iterator operator++(int) { + auto tmp = *this; + ++it_; + return tmp; + } + + rvalue_iterator& operator--() { + --it_; + return *this; + } + + rvalue_iterator operator--(int) { + auto tmp = *this; + --it_; + return tmp; + } + + rvalue_iterator operator+(difference_type n) const { + auto tmp = *this; + tmp.it += n; + return tmp; + } + + friend rvalue_iterator operator+(difference_type n, rvalue_iterator iter) { + iter += n; + return iter; + } + + rvalue_iterator operator-(difference_type n) const { + auto tmp = *this; + tmp.it -= n; + return tmp; + } + + difference_type operator-(const rvalue_iterator& other) const { return it_ - other.it_; } + + rvalue_iterator& operator+=(difference_type n) { + it_ += n; + return *this; + } + + rvalue_iterator& operator-=(difference_type n) { + it_ -= n; + return *this; + } + + reference operator[](difference_type n) const { return std::move(it_[n]); } + + auto operator<=>(const rvalue_iterator&) const noexcept = default; + +private: + T* it_; +}; + // Proxy // ====================================================================== // Proxy that can wrap a value or a reference. It simulates C++23's tuple