[libcxx-commits] [libcxx] [libc++] P2502R2: `std::generator` (PR #92213)
Xiaoyang Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Wed May 15 09:26:58 PDT 2024
https://github.com/xiaoyang-sde updated https://github.com/llvm/llvm-project/pull/92213
>From c1fc9cb0b75b81d0a91364f4bbffb64bf2c6de86 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 15 May 2024 00:36:58 -0400
Subject: [PATCH 1/3] [libc++][ranges] implement 'ranges::elements_of'
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__ranges/elements_of.h | 55 +++++++++++++++++++
libcxx/include/module.modulemap | 1 +
libcxx/include/ranges | 5 ++
libcxx/modules/std/ranges.inc | 4 +-
.../range.elementsof/elements_of.pass.cpp | 40 ++++++++++++++
6 files changed, 105 insertions(+), 1 deletion(-)
create mode 100644 libcxx/include/__ranges/elements_of.h
create mode 100644 libcxx/test/std/ranges/range.utility/range.elementsof/elements_of.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 01e9c247560ca..2be32fee2a9fd 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -647,6 +647,7 @@ set(files
__ranges/data.h
__ranges/drop_view.h
__ranges/drop_while_view.h
+ __ranges/elements_of.h
__ranges/elements_view.h
__ranges/empty.h
__ranges/empty_view.h
diff --git a/libcxx/include/__ranges/elements_of.h b/libcxx/include/__ranges/elements_of.h
new file mode 100644
index 0000000000000..f446d3c12d67e
--- /dev/null
+++ b/libcxx/include/__ranges/elements_of.h
@@ -0,0 +1,55 @@
+// -*- 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_ELEMENTS_OF_H
+#define _LIBCPP___RANGES_ELEMENTS_OF_H
+
+#include <__config>
+#include <__memory/allocator.h>
+#include <__ranges/concepts.h>
+#include <__utility/move.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace ranges {
+
+template <range _Range, class _Allocator = allocator<byte>>
+struct elements_of {
+ _LIBCPP_NO_UNIQUE_ADDRESS _Range range;
+ _LIBCPP_NO_UNIQUE_ADDRESS _Allocator allocator;
+
+ // This explicit constructor is required because AppleClang 15 hasn't implement P0960R3
+ _LIBCPP_HIDE_FROM_ABI explicit constexpr elements_of(_Range __range, _Allocator __alloc = _Allocator())
+ : range(std::move(__range)), allocator(std::move(__alloc)) {}
+};
+
+template <class _Range, class _Allocator = allocator<byte>>
+// This explicit constraint is required because AppleClang 15 might not deduce the correct type for `_Range` without it
+ requires range<_Range&&>
+elements_of(_Range&&, _Allocator = _Allocator()) -> elements_of<_Range&&, _Allocator>;
+
+} // namespace ranges
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___RANGES_ELEMENTS_OF_H
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 70dac2f19846b..6bc9403db2b59 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1701,6 +1701,7 @@ module std_private_ranges_dangling [system] { header "__ranges
module std_private_ranges_data [system] { header "__ranges/data.h" }
module std_private_ranges_drop_view [system] { header "__ranges/drop_view.h" }
module std_private_ranges_drop_while_view [system] { header "__ranges/drop_while_view.h" }
+module std_private_ranges_elements_of [system] { header "__ranges/elements_of.h" }
module std_private_ranges_elements_view [system] { header "__ranges/elements_view.h" }
module std_private_ranges_empty [system] { header "__ranges/empty.h" }
module std_private_ranges_empty_view [system] { header "__ranges/empty_view.h" }
diff --git a/libcxx/include/ranges b/libcxx/include/ranges
index 07a525ed8641f..a8fbfc462bf0d 100644
--- a/libcxx/include/ranges
+++ b/libcxx/include/ranges
@@ -116,6 +116,10 @@ namespace std::ranges {
// [range.dangling], dangling iterator handling
struct dangling;
+ // [range.elementsof], class template elements_of
+ template<range R, class Allocator = allocator<byte>>
+ struct elements_of;
+
template<range R>
using borrowed_iterator_t = see below;
@@ -392,6 +396,7 @@ namespace std {
#include <__ranges/data.h>
#include <__ranges/drop_view.h>
#include <__ranges/drop_while_view.h>
+#include <__ranges/elements_of.h>
#include <__ranges/elements_view.h>
#include <__ranges/empty.h>
#include <__ranges/empty_view.h>
diff --git a/libcxx/modules/std/ranges.inc b/libcxx/modules/std/ranges.inc
index f71efe948ede1..82055c2ddbfbb 100644
--- a/libcxx/modules/std/ranges.inc
+++ b/libcxx/modules/std/ranges.inc
@@ -83,8 +83,10 @@ export namespace std {
// [range.dangling], dangling iterator handling
using std::ranges::dangling;
+#if _LIBCPP_STD_VER >= 23
// [range.elementsof], class template elements_of
- // using std::ranges::elements_of;
+ using std::ranges::elements_of;
+#endif
using std::ranges::borrowed_iterator_t;
diff --git a/libcxx/test/std/ranges/range.utility/range.elementsof/elements_of.pass.cpp b/libcxx/test/std/ranges/range.utility/range.elementsof/elements_of.pass.cpp
new file mode 100644
index 0000000000000..378bcb0a2f13f
--- /dev/null
+++ b/libcxx/test/std/ranges/range.utility/range.elementsof/elements_of.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, c++20
+
+// std::ranges::elements_of;
+
+#include <ranges>
+
+#include <concepts>
+#include <memory>
+#include <vector>
+
+constexpr bool test() {
+ {
+ auto elements_of = std::ranges::elements_of(std::vector<int>());
+ static_assert(
+ std::same_as<decltype(elements_of), std::ranges::elements_of<std::vector<int>&&, std::allocator<std::byte>>>);
+ static_assert(std::same_as<decltype(elements_of.range), std::vector<int>&&>);
+ static_assert(std::same_as<decltype(elements_of.allocator), std::allocator<std::byte>>);
+ }
+ {
+ auto elements_of = std::ranges::elements_of(std::vector<int>(), std::allocator<int>());
+ static_assert(
+ std::same_as<decltype(elements_of), std::ranges::elements_of<std::vector<int>&&, std::allocator<int>>>);
+ static_assert(std::same_as<decltype(elements_of.range), std::vector<int>&&>);
+ static_assert(std::same_as<decltype(elements_of.allocator), std::allocator<int>>);
+ }
+ return true;
+}
+
+int main() {
+ test();
+ static_assert(test());
+}
>From 176b313133ecdc07a774de28551c4d91fe7c4d7e Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 15 May 2024 00:39:07 -0400
Subject: [PATCH 2/3] [libc++] implement 'std::generator'
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/generator | 377 ++++++++++++++++++
.../ranges/coro.generator/generator.pass.cpp | 40 ++
.../ranges/coro.generator/recursive.pass.cpp | 92 +++++
4 files changed, 510 insertions(+)
create mode 100644 libcxx/include/generator
create mode 100644 libcxx/test/std/ranges/coro.generator/generator.pass.cpp
create mode 100644 libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 2be32fee2a9fd..5344dc06decfb 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -951,6 +951,7 @@ set(files
fstream
functional
future
+ generator
initializer_list
inttypes.h
iomanip
diff --git a/libcxx/include/generator b/libcxx/include/generator
new file mode 100644
index 0000000000000..6542e4c02499d
--- /dev/null
+++ b/libcxx/include/generator
@@ -0,0 +1,377 @@
+// -*- 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_GENERATOR
+#define _LIBCPP_GENERATOR
+
+#include <__assert>
+#include <__concepts/constructible.h>
+#include <__concepts/convertible_to.h>
+#include <__config>
+#include <__coroutine/coroutine_handle.h>
+#include <__coroutine/coroutine_traits.h>
+#include <__coroutine/noop_coroutine_handle.h>
+#include <__coroutine/trivial_awaitables.h>
+#include <__exception/exception_ptr.h>
+#include <__iterator/default_sentinel.h>
+#include <__memory/addressof.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/allocator_traits.h>
+#include <__ranges/concepts.h>
+#include <__ranges/elements_of.h>
+#include <__ranges/view_interface.h>
+#include <__type_traits/add_pointer.h>
+#include <__type_traits/common_reference.h>
+#include <__type_traits/conditional.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__type_traits/is_object.h>
+#include <__type_traits/is_reference.h>
+#include <__type_traits/is_void.h>
+#include <__type_traits/remove_cvref.h>
+#include <__utility/exchange.h>
+#include <__utility/swap.h>
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class _Ref, class _Val>
+using __gen_val = conditional_t<is_void_v<_Val>, remove_cvref_t<_Ref>, _Val>;
+
+template <class _Ref, class _Val>
+using __gen_ref = conditional_t<is_void_v<_Val>, _Ref&&, _Ref>;
+
+template <class _Ref, class _Val>
+using __gen_yielded =
+ conditional_t<is_reference_v<__gen_ref<_Ref, _Val>>, __gen_ref<_Ref, _Val>, const __gen_ref<_Ref, _Val>&>;
+
+template <class, class, class>
+class generator;
+
+template <class _Yielded>
+class __gen_promise_base {
+private:
+ template <class, class, class>
+ friend class generator;
+
+ template <class, class>
+ friend class __gen_iter;
+
+ // Each promise object stores either a `__root_data` when associated with a root generator, or a `__recursive_data`
+ // when associated with a generator that is yielded recursively.
+ struct __root_data {
+ // The client code has access only to the iterator of the root generator. Thus, the root generator must store the
+ // yielded values of recursively-yielded generators, which will then be returned when the client code dereferences
+ // the iterator.
+ add_pointer_t<_Yielded> __value_ptr;
+ // The client code has access only to the iterator of the root generator. Thus, the root generator needs to identify
+ // which generator is currently active. This active generator will then be resumed when the client code increments
+ // the iterator.
+ std::coroutine_handle<__gen_promise_base> __active;
+ };
+
+ struct __recursive_data {
+ std::exception_ptr __exception;
+ std::coroutine_handle<__gen_promise_base> __parent;
+ std::coroutine_handle<__gen_promise_base> __root;
+ };
+
+ union __union {
+ __root_data __root;
+ __recursive_data __recursive;
+
+ ~__union() noexcept {}
+ } __data_;
+
+ // The `__tag_` stores the active member of the `__data_` union:
+ // - `false` indicates that the active member is `__root`.
+ // - `true` indicates that the active member is `__recursive`.
+ // This field can be omitted because `__recursive_data.__root` can store the active member.
+ bool __tag_;
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_root() noexcept { return !__tag_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __root_data& __get_root_data() noexcept {
+ _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+ return __data_.__root;
+ }
+ _LIBCPP_HIDE_FROM_ABI void __set_root_tag() noexcept { __tag_ = false; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool __is_recursive() noexcept { return __tag_; }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __recursive_data& __get_recursive_data() noexcept {
+ _LIBCPP_ASSERT_INTERNAL(__is_recursive(), "the active member of `__data_` is not `__recursive`");
+ return __data_.__recursive;
+ }
+ _LIBCPP_HIDE_FROM_ABI void __set_recursive_tag() noexcept { __tag_ = true; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI std::coroutine_handle<__gen_promise_base>& __active() noexcept {
+ _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+ return __get_root_data().__active;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __value_ptr() noexcept {
+ _LIBCPP_ASSERT_INTERNAL(__is_root(), "the active member of `__data_` is not `__root`");
+ return __get_root_data().__value_ptr;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI add_pointer_t<_Yielded>& __root_value_ptr() noexcept {
+ if (__is_root()) {
+ return __value_ptr();
+ }
+ return __get_recursive_data().__root.promise().__value_ptr();
+ }
+
+ struct __element_awaiter {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+ template <class _Promise>
+ _LIBCPP_HIDE_FROM_ABI void await_suspend(coroutine_handle<_Promise> __current) noexcept {
+ __current.promise().__root_value_ptr() = std::addressof(__value);
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+
+ remove_cvref_t<_Yielded> __value;
+ };
+
+ struct __final_awaiter {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return false; }
+
+ template <class _Promise>
+ _LIBCPP_HIDE_FROM_ABI coroutine_handle<> await_suspend(coroutine_handle<_Promise> __current) noexcept {
+ // Checks if the current generator is recursively-yielded
+ if (__current.promise().__is_recursive()) {
+ auto&& __recursive_data = __current.promise().__get_recursive_data();
+ auto __parent = __recursive_data.__parent;
+ // Updates the active generator to its parent, allowing the client code to resume it later
+ __recursive_data.__root.promise().__active() = __parent;
+ // Transfers execution to its parent, which is the generator that yields it
+ return __parent;
+ }
+ return std::noop_coroutine();
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void await_resume() noexcept {}
+ };
+
+ template <class _Ref, class _Val, class _Allocator>
+ struct __recursive_awaiter {
+ generator<_Ref, _Val, _Allocator> __gen;
+
+ _LIBCPP_HIDE_FROM_ABI explicit __recursive_awaiter(generator<_Ref, _Val, _Allocator>&& __gen) noexcept
+ : __gen{std::move(__gen)} {}
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return !__gen.__coroutine_; }
+
+ template <class _Promise>
+ _LIBCPP_HIDE_FROM_ABI coroutine_handle<__gen_promise_base>
+ await_suspend(coroutine_handle<_Promise> __current) noexcept {
+ // Stores a `__recursive_data` in the promise object associated with `__gen`
+ auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen.__coroutine_.address());
+ __recursive.promise().__set_recursive_tag();
+ auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+
+ // Sets `__recursive_data.__parent` to the current generator
+ auto __parent = coroutine_handle<__gen_promise_base>::from_address(__current.address());
+ __recursive_data.__parent = __parent;
+
+ // Sets `__recursive_data.__root` to the current generator if it's a root generator, or to the root generator of
+ // the current generator otherwise
+ if (__parent.promise().__is_recursive()) {
+ __recursive_data.__root = __parent.promise().__get_recursive_data().__root;
+ } else {
+ __recursive_data.__root = __parent;
+ }
+
+ // Updates the active generator to `__gen`, allowing the client code to resume it later
+ __recursive_data.__root.promise().__active() = __recursive;
+
+ // Transfers execution to `__gen`
+ return __recursive;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void await_resume() {
+ auto __recursive = coroutine_handle<__gen_promise_base>::from_address(__gen.__coroutine_.address());
+ auto&& __recursive_data = __recursive.promise().__get_recursive_data();
+ if (__recursive_data.__exception) {
+ std::rethrow_exception(std::move(__recursive_data.__exception));
+ }
+ }
+ };
+
+public:
+ _LIBCPP_HIDE_FROM_ABI __gen_promise_base() noexcept
+ : __data_{.__root =
+ {
+ .__value_ptr = nullptr,
+ .__active = coroutine_handle<__gen_promise_base>::from_promise(*this),
+ }},
+ __tag_{false} {}
+
+ _LIBCPP_HIDE_FROM_ABI ~__gen_promise_base() noexcept {
+ if (__is_root()) {
+ __data_.__root.~__root_data();
+ } else {
+ __data_.__recursive.~__recursive_data();
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI suspend_always initial_suspend() const noexcept { return {}; }
+
+ _LIBCPP_HIDE_FROM_ABI __final_awaiter final_suspend() noexcept { return {}; }
+
+ _LIBCPP_HIDE_FROM_ABI suspend_always yield_value(_Yielded __value) noexcept {
+ __root_value_ptr() = std::addressof(__value);
+ return {};
+ }
+
+ _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value)
+ requires is_rvalue_reference_v<_Yielded> &&
+ constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
+ {
+ return __element_awaiter{.__value = __value};
+ }
+
+ template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
+ requires same_as<__gen_yielded<_Ref2, _Val2>, _Yielded>
+ _LIBCPP_HIDE_FROM_ABI auto
+ yield_value(ranges::elements_of<generator<_Ref2, _Val2, _Allocator2>&&, _Unused> __elements) noexcept {
+ return __recursive_awaiter<_Ref2, _Val2, _Allocator2>{std::move(__elements.range)};
+ }
+
+ template <ranges::input_range _Range, class _Allocator>
+ requires convertible_to<ranges::range_reference_t<_Range>, _Yielded>
+ _LIBCPP_HIDE_FROM_ABI auto yield_value(ranges::elements_of<_Range, _Allocator> __range) {
+ auto __lambda =
+ [](allocator_arg_t, _Allocator, ranges::iterator_t<_Range> __i, ranges::sentinel_t<_Range> __s) static
+ -> generator<_Yielded, ranges::range_value_t<_Range>, _Allocator> {
+ for (; __i != __s; ++__i) {
+ co_yield static_cast<_Yielded>(*__i);
+ }
+ };
+ return yield_value(ranges::elements_of(
+ __lambda(allocator_arg, __range.allocator, ranges::begin(__range.range), ranges::end(__range.range))));
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void await_transform() = delete;
+
+ _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
+
+ _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
+ if (__is_root()) {
+ throw;
+ } else {
+ __get_recursive_data().__exception = std::current_exception();
+ }
+ }
+};
+
+template <class _Ref, class _Val>
+class __gen_iter {
+private:
+ using __val = __gen_val<_Ref, _Val>;
+ using __ref = __gen_ref<_Ref, _Val>;
+
+public:
+ using value_type = __val;
+ using difference_type = ptrdiff_t;
+
+ _LIBCPP_HIDE_FROM_ABI explicit __gen_iter(
+ coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine) noexcept
+ : __coroutine_{__coroutine} {}
+
+ _LIBCPP_HIDE_FROM_ABI __gen_iter(__gen_iter&& __other) noexcept
+ : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+ _LIBCPP_HIDE_FROM_ABI __gen_iter& operator=(__gen_iter&& __other) noexcept {
+ __coroutine_ = std::exchange(__other.__coroutine_, {});
+ return *this;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __ref operator*() const noexcept(is_nothrow_copy_constructible_v<__ref>) {
+ return static_cast<__ref>(*__coroutine_.promise().__value_ptr());
+ }
+
+ _LIBCPP_HIDE_FROM_ABI __gen_iter& operator++() {
+ __coroutine_.promise().__active().resume();
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool operator==(default_sentinel_t) const noexcept { return __coroutine_.done(); }
+
+private:
+ coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine_;
+};
+
+template <class _Ref, class _Val = void, class _Allocator = void>
+class generator : public ranges::view_interface<generator<_Ref, _Val, _Allocator>> {
+private:
+ using __val = __gen_val<_Ref, _Val>;
+ static_assert(same_as<remove_cvref_t<__val>, __val> && is_object_v<__val>);
+
+ using __ref = __gen_ref<_Ref, _Val>;
+ static_assert(is_reference_v<__ref> ||
+ (same_as<remove_cvref_t<__ref>, __ref> && is_object_v<__ref> && copy_constructible<__ref>));
+
+ using __rref = conditional_t<is_lvalue_reference_v<_Ref>, remove_reference_t<_Ref>&&, _Ref>;
+ static_assert(common_reference_with<__ref&&, __val&> && common_reference_with<__ref&&, __rref&&> &&
+ common_reference_with<__rref&&, const __val&>);
+
+ template <class>
+ friend class __gen_promise_base;
+
+public:
+ using yielded = __gen_yielded<_Ref, _Val>;
+
+ struct promise_type : __gen_promise_base<yielded> {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI generator get_return_object() noexcept {
+ return generator{coroutine_handle<promise_type>::from_promise(*this)};
+ }
+ };
+
+ _LIBCPP_HIDE_FROM_ABI generator(const generator&) = delete;
+ _LIBCPP_HIDE_FROM_ABI generator(generator&& __other) noexcept
+ : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+ _LIBCPP_HIDE_FROM_ABI ~generator() {
+ if (__coroutine_) {
+ __coroutine_.destroy();
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI generator& operator=(generator __other) noexcept {
+ std::swap(__coroutine_, __other.__coroutine_);
+ return *this;
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI __gen_iter<_Ref, _Val> begin() {
+ auto __coroutine = std::coroutine_handle<__gen_promise_base<yielded>>::from_promise(__coroutine_.promise());
+ __coroutine.resume();
+ return __gen_iter<_Ref, _Val>{__coroutine};
+ }
+
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI default_sentinel_t end() const noexcept { return {}; }
+
+private:
+ _LIBCPP_HIDE_FROM_ABI explicit generator(coroutine_handle<promise_type> __coroutine) noexcept
+ : __coroutine_{__coroutine} {}
+
+ coroutine_handle<promise_type> __coroutine_ = nullptr;
+};
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#endif
diff --git a/libcxx/test/std/ranges/coro.generator/generator.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
new file mode 100644
index 0000000000000..c7e280f74b70a
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.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, c++20
+
+// std::generator
+
+#include <generator>
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+ int a = 0;
+ int b = 1;
+ while (true) {
+ co_yield std::exchange(a, std::exchange(b, a + b));
+ }
+}
+
+bool test() {
+ {
+ std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+ auto fib_vec = fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+ assert(fib_vec == expected_fib_vec);
+ }
+ return true;
+}
+
+int main() {
+ test();
+ return 0;
+}
diff --git a/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
new file mode 100644
index 0000000000000..f02f5439a77a2
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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::generator
+
+#include <generator>
+
+#include <cassert>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+std::generator<int> fib() {
+ int a = 0;
+ int b = 1;
+ while (true) {
+ co_yield std::exchange(a, std::exchange(b, a + b));
+ }
+}
+
+std::generator<int> recursive_fib(int depth) {
+ if (depth == 0) {
+ co_yield std::ranges::elements_of(fib());
+ } else {
+ co_yield std::ranges::elements_of(recursive_fib(depth - 1));
+ }
+};
+
+struct tree_node {
+ tree_node* left;
+ tree_node* right;
+ int element;
+
+ ~tree_node() {
+ delete left;
+ delete right;
+ }
+};
+
+tree_node* build_tree(int depth) {
+ if (depth == 0) {
+ return nullptr;
+ }
+
+ tree_node* root = new tree_node();
+ root->element = depth;
+ root->left = build_tree(depth - 1);
+ root->right = build_tree(depth - 1);
+ return root;
+}
+
+std::generator<int> traversal(tree_node* node) {
+ if (node == nullptr) {
+ co_return;
+ }
+ co_yield std::ranges::elements_of(traversal(node->left));
+ co_yield node->element;
+ co_yield std::ranges::elements_of(traversal(node->right));
+}
+
+bool test() {
+ {
+ std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+ {
+ auto fib_vec = recursive_fib(1) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+ assert(fib_vec == expected_fib_vec);
+ }
+ {
+ auto fib_vec = recursive_fib(42) | std::views::take(5) | std::ranges::to<std::vector<int>>();
+ assert(fib_vec == expected_fib_vec);
+ }
+ }
+ {
+ tree_node* tree_root = build_tree(10);
+ auto node_vec = traversal(tree_root) | std::ranges::to<std::vector<int>>();
+ assert(node_vec.size() == 1023);
+ delete tree_root;
+ }
+ return true;
+}
+
+int main() {
+ test();
+ return 0;
+}
>From 652f44fdf37e1124200f66d590f0be75e55366e5 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 15 May 2024 12:23:53 -0400
Subject: [PATCH 3/3] [libc++] implement 'std::generator'
---
libcxx/include/generator | 20 ++++----
.../test/libcxx/transitive_includes/cxx03.csv | 9 ++++
.../test/libcxx/transitive_includes/cxx11.csv | 9 ++++
.../test/libcxx/transitive_includes/cxx14.csv | 9 ++++
.../test/libcxx/transitive_includes/cxx17.csv | 9 ++++
.../test/libcxx/transitive_includes/cxx20.csv | 9 ++++
.../test/libcxx/transitive_includes/cxx23.csv | 9 ++++
.../test/libcxx/transitive_includes/cxx26.csv | 9 ++++
.../coro.generator/generator.compile.pass.cpp | 49 +++++++++++++++++++
.../ranges/coro.generator/generator.pass.cpp | 22 +++++++--
.../ranges/coro.generator/recursive.pass.cpp | 7 ++-
11 files changed, 148 insertions(+), 13 deletions(-)
create mode 100644 libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
diff --git a/libcxx/include/generator b/libcxx/include/generator
index 6542e4c02499d..c7637d70a70d6 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -206,11 +206,10 @@ private:
public:
_LIBCPP_HIDE_FROM_ABI __gen_promise_base() noexcept
- : __data_{.__root =
- {
- .__value_ptr = nullptr,
- .__active = coroutine_handle<__gen_promise_base>::from_promise(*this),
- }},
+ : __data_{.__root{
+ .__value_ptr{nullptr},
+ .__active{coroutine_handle<__gen_promise_base>::from_promise(*this)},
+ }},
__tag_{false} {}
_LIBCPP_HIDE_FROM_ABI ~__gen_promise_base() noexcept {
@@ -230,11 +229,12 @@ public:
return {};
}
- _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value)
+ _LIBCPP_HIDE_FROM_ABI auto yield_value(const remove_reference_t<_Yielded>& __value) noexcept(
+ is_nothrow_constructible_v<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>)
requires is_rvalue_reference_v<_Yielded> &&
constructible_from<remove_cvref_t<_Yielded>, const remove_reference_t<_Yielded>&>
{
- return __element_awaiter{.__value = __value};
+ return __element_awaiter{.__value{__value}};
}
template <class _Ref2, class _Val2, class _Allocator2, class _Unused>
@@ -304,7 +304,9 @@ public:
_LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
- [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool operator==(default_sentinel_t) const noexcept { return __coroutine_.done(); }
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const __gen_iter& __iter, default_sentinel_t) noexcept {
+ return __iter.__coroutine_.done();
+ }
private:
coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine_;
@@ -363,7 +365,7 @@ private:
_LIBCPP_HIDE_FROM_ABI explicit generator(coroutine_handle<promise_type> __coroutine) noexcept
: __coroutine_{__coroutine} {}
- coroutine_handle<promise_type> __coroutine_ = nullptr;
+ coroutine_handle<promise_type> __coroutine_;
};
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 92601fab5b773..913eebb64f269 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -370,6 +370,15 @@ future thread
future type_traits
future typeinfo
future version
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator typeinfo
initializer_list cstddef
iomanip istream
iomanip version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c05eb42deb9a1..f205bdc232479 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -372,6 +372,15 @@ future thread
future type_traits
future typeinfo
future version
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator typeinfo
initializer_list cstddef
iomanip istream
iomanip version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 09252b7b7d2db..85237f65959d0 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -375,6 +375,15 @@ future thread
future type_traits
future typeinfo
future version
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator typeinfo
initializer_list cstddef
iomanip istream
iomanip version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 09252b7b7d2db..85237f65959d0 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -375,6 +375,15 @@ future thread
future type_traits
future typeinfo
future version
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator typeinfo
initializer_list cstddef
iomanip istream
iomanip version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index ce4ccc3d11615..505e1363d3f53 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -386,6 +386,15 @@ future thread
future type_traits
future typeinfo
future version
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator typeinfo
initializer_list cstddef
iomanip istream
iomanip version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 62d931c0eebad..5af8dc20b70eb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -259,6 +259,15 @@ future string
future thread
future typeinfo
future version
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator typeinfo
initializer_list cstddef
iomanip istream
iomanip version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index f68249aeec78c..103e9487ec0c2 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -282,6 +282,15 @@ future string
future thread
future typeinfo
future version
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator typeinfo
initializer_list cstddef
iomanip istream
iomanip version
diff --git a/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
new file mode 100644
index 0000000000000..e1725b87e8cd2
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <generator>
+
+// template<class Ref, class V = void, class Allocator = void>
+// class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+#include <type_traits>
+
+template <class G, class V, class R, class RR>
+constexpr bool conformance() {
+ static_assert(std::ranges::range<G>);
+ static_assert(std::ranges::view<G>);
+ static_assert(std::ranges::input_range<G>);
+ static_assert(!std::ranges::forward_range<G>);
+ static_assert(!std::ranges::borrowed_range<G>);
+
+ static_assert(std::same_as<std::ranges::range_value_t<G>, V>);
+ static_assert(std::same_as<std::ranges::range_reference_t<G>, R>);
+ static_assert(std::same_as<std::ranges::range_rvalue_reference_t<G>, RR>);
+
+ return true;
+}
+
+static_assert(conformance<std::generator<int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int, int>, int, int, int>());
+
+static_assert(conformance<std::generator<int&>, int, int&, int&&>());
+static_assert(conformance<std::generator<int&, int>, int, int&, int&&>());
+static_assert(conformance<std::generator<const int&>, int, const int&, const int&&>());
+static_assert(conformance<std::generator<const int&, int>, int, const int&, const int&&>());
+
+static_assert(conformance<std::generator<int&&>, int, int&&, int&&>());
+static_assert(conformance<std::generator<int&&, int>, int, int&&, int&&>());
+static_assert(conformance<std::generator<const int&&>, int, const int&&, const int&&>());
+static_assert(conformance<std::generator<const int&&, int>, int, const int&&, const int&&>());
diff --git a/libcxx/test/std/ranges/coro.generator/generator.pass.cpp b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
index c7e280f74b70a..e01d79618b56d 100644
--- a/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
+++ b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
@@ -8,7 +8,10 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// std::generator
+// <generator>
+
+// template<class Ref, class V = void, class Allocator = void>
+// class generator;
#include <generator>
@@ -25,11 +28,24 @@ std::generator<int> fib() {
}
}
+std::generator<const int&> range_fib() {
+ co_yield std::ranges::elements_of(std::vector<int>{0, 1});
+ co_yield std::ranges::elements_of(std::vector<int>{1, 2});
+ co_yield std::ranges::elements_of(std::vector<int>{3, 5});
+ co_yield std::ranges::elements_of(std::vector<int>{5, 8});
+}
+
bool test() {
{
std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
- auto fib_vec = fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
- assert(fib_vec == expected_fib_vec);
+ {
+ auto fib_vec = fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+ assert(fib_vec == expected_fib_vec);
+ }
+ {
+ auto fib_vec = range_fib() | std::views::take(5) | std::ranges::to<std::vector<int>>();
+ assert(fib_vec == expected_fib_vec);
+ }
}
return true;
}
diff --git a/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
index f02f5439a77a2..51365eabec900 100644
--- a/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
+++ b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
@@ -8,11 +8,15 @@
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
-// std::generator
+// <generator>
+
+// template<class Ref, class V = void, class Allocator = void>
+// class generator;
#include <generator>
#include <cassert>
+#include <numeric>
#include <ranges>
#include <utility>
#include <vector>
@@ -81,6 +85,7 @@ bool test() {
tree_node* tree_root = build_tree(10);
auto node_vec = traversal(tree_root) | std::ranges::to<std::vector<int>>();
assert(node_vec.size() == 1023);
+ assert(std::reduce(node_vec.cbegin(), node_vec.cend()) == 2036);
delete tree_root;
}
return true;
More information about the libcxx-commits
mailing list