[libcxx-commits] [libcxx] [libc++] P2502R2: `std::generator` (PR #92213)

Xiaoyang Liu via libcxx-commits libcxx-commits at lists.llvm.org
Mon May 20 08:19:34 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 01/10] [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 02/10] [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 03/10] [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;

>From 9748a32b918fa1500822388a3f84acbb58aa211f Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Wed, 15 May 2024 12:53:18 -0400
Subject: [PATCH 04/10] [libc++] implement 'std::generator'

---
 libcxx/include/__std_clang_module         |  1 +
 libcxx/include/generator                  | 22 ++++++++++++++++------
 libcxx/include/module.modulemap           |  4 ++++
 libcxx/modules/std.compat.cppm.in         |  3 ---
 libcxx/modules/std.cppm.in                |  4 +---
 libcxx/modules/std/generator.inc          |  3 ++-
 libcxx/utils/libcxx/header_information.py |  1 -
 7 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/libcxx/include/__std_clang_module b/libcxx/include/__std_clang_module
index 18d6ce6b46c1f..e18947350adc2 100644
--- a/libcxx/include/__std_clang_module
+++ b/libcxx/include/__std_clang_module
@@ -104,6 +104,7 @@
 #if !defined(_LIBCPP_HAS_NO_THREADS)
 #  include <future>
 #endif
+#include <generator>
 #include <initializer_list>
 #include <inttypes.h>
 #if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
diff --git a/libcxx/include/generator b/libcxx/include/generator
index c7637d70a70d6..ebdc264fcb071 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -11,8 +11,10 @@
 #define _LIBCPP_GENERATOR
 
 #include <__assert>
+#include <__concepts/common_reference_with.h>
 #include <__concepts/constructible.h>
 #include <__concepts/convertible_to.h>
+#include <__concepts/same_as.h>
 #include <__config>
 #include <__coroutine/coroutine_handle.h>
 #include <__coroutine/coroutine_traits.h>
@@ -23,6 +25,7 @@
 #include <__memory/addressof.h>
 #include <__memory/allocator_arg_t.h>
 #include <__memory/allocator_traits.h>
+#include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__ranges/elements_of.h>
 #include <__ranges/view_interface.h>
@@ -34,9 +37,18 @@
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_cvref.h>
+#include <__type_traits/remove_reference.h>
 #include <__utility/exchange.h>
+#include <__utility/move.h>
 #include <__utility/swap.h>
 
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 #if _LIBCPP_STD_VER >= 23
 
 _LIBCPP_BEGIN_NAMESPACE_STD
@@ -86,7 +98,7 @@ private:
     __root_data __root;
     __recursive_data __recursive;
 
-    ~__union() noexcept {}
+    _LIBCPP_HIDE_FROM_ABI ~__union() noexcept {}
   } __data_;
 
   // The `__tag_` stores the active member of the `__data_` union:
@@ -370,10 +382,8 @@ private:
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif
+#endif // _LIBCPP_STD_VER >= 23
 
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
+_LIBCPP_POP_MACROS
 
-#endif
+#endif // _LIBCPP_GENERATOR
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 6bc9403db2b59..a46d283e6d273 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -99,6 +99,10 @@ module std_future [system] {
   header "future"
   export *
 }
+module std_generator [system] {
+  header "generator"
+  export *
+}
 module std_initializer_list [system] {
   header "initializer_list"
   export *
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index b44dbab25c74b..b1ce8fa37820f 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -63,9 +63,6 @@ module;
 #  if __has_include(<flat_set>)
 #    error "please update the header information for <flat_set> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<flat_set>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std.cppm.in b/libcxx/modules/std.cppm.in
index b8d89130aae98..0111c739d0cda 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -79,6 +79,7 @@ module;
 #if !defined(_LIBCPP_HAS_NO_THREADS)
 #  include <future>
 #endif
+#include <generator>
 #include <initializer_list>
 #if !defined(_LIBCPP_HAS_NO_LOCALIZATION)
 #  include <iomanip>
@@ -185,9 +186,6 @@ module;
 #  if __has_include(<flat_set>)
 #    error "please update the header information for <flat_set> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<flat_set>)
-#  if __has_include(<generator>)
-#    error "please update the header information for <generator> in headers_not_available in utils/libcxx/header_information.py"
-#  endif // __has_include(<generator>)
 #  if __has_include(<hazard_pointer>)
 #    error "please update the header information for <hazard_pointer> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<hazard_pointer>)
diff --git a/libcxx/modules/std/generator.inc b/libcxx/modules/std/generator.inc
index 43fb0daf5c659..f7102b138b078 100644
--- a/libcxx/modules/std/generator.inc
+++ b/libcxx/modules/std/generator.inc
@@ -8,7 +8,8 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+  // [coro.generator], class template generator
   using std::generator;
 #endif
 } // namespace std
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index e2165d6ab80b0..bdfe17f5c9a03 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -141,7 +141,6 @@
     "debugging",
     "flat_map",
     "flat_set",
-    "generator",
     "hazard_pointer",
     "linalg",
     "rcu",

>From 457f78cebe47bb3d32f499369fa571371e30f40b Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Thu, 16 May 2024 00:14:11 -0400
Subject: [PATCH 05/10] [libc++] implement 'std::generator'

---
 libcxx/include/generator | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index ebdc264fcb071..c61e43fd7ff32 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -173,18 +173,16 @@ private:
 
   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)} {}
+        : __gen_{std::move(__gen)} {}
 
-    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI bool await_ready() noexcept { return !__gen.__coroutine_; }
+    [[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());
+      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();
 
@@ -208,12 +206,15 @@ private:
     }
 
     _LIBCPP_HIDE_FROM_ABI void await_resume() {
-      auto __recursive        = coroutine_handle<__gen_promise_base>::from_address(__gen.__coroutine_.address());
+      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));
       }
     }
+
+  private:
+    generator<_Ref, _Val, _Allocator> __gen_;
   };
 
 public:
@@ -275,11 +276,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
       __get_recursive_data().__exception = std::current_exception();
     }
+#  endif
   }
 };
 

>From c72c94dfa5894fab046126a1a59e4d03681b72ce Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Fri, 17 May 2024 22:48:14 -0400
Subject: [PATCH 06/10] [libc++] implement 'std::generator'

---
 libcxx/include/generator                      | 204 +++++++++++++++++-
 .../ranges/coro.generator/allocator.pass.cpp  | 123 +++++++++++
 2 files changed, 326 insertions(+), 1 deletion(-)
 create mode 100644 libcxx/test/std/ranges/coro.generator/allocator.pass.cpp

diff --git a/libcxx/include/generator b/libcxx/include/generator
index c61e43fd7ff32..1931a4c9bdf85 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -10,6 +10,22 @@
 #ifndef _LIBCPP_GENERATOR
 #define _LIBCPP_GENERATOR
 
+/*
+
+namespace std {
+  // [coro.generator.class], class template generator
+  template<class Ref, class V = void, class Allocator = void>
+    class generator;
+
+  namespace pmr {
+    template<class R, class V = void>
+      using generator = std::generator<R, V, polymorphic_allocator<>>;
+  }
+}
+
+*/
+
+#include <__algorithm/max.h>
 #include <__assert>
 #include <__concepts/common_reference_with.h>
 #include <__concepts/constructible.h>
@@ -25,6 +41,8 @@
 #include <__memory/addressof.h>
 #include <__memory/allocator_arg_t.h>
 #include <__memory/allocator_traits.h>
+#include <__memory/construct_at.h>
+#include <__memory_resource/polymorphic_allocator.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
 #include <__ranges/elements_of.h>
@@ -286,6 +304,183 @@ public:
   }
 };
 
+struct alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__) __gen_aligned_block {
+  char __data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
+
+  _LIBCPP_HIDE_FROM_ABI static size_t __count(const size_t __size) noexcept {
+    return (__size + sizeof(__gen_aligned_block) - 1) / sizeof(__gen_aligned_block);
+  };
+};
+
+template <class _Allocator>
+concept __gen_stateless_allocator =
+    default_initializable<_Allocator> && allocator_traits<_Allocator>::is_always_equal::value;
+
+template <class _Allocator>
+class __gen_promise_allocator {
+private:
+  using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+  using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+  using __rebind_pointer = allocator_traits<__rebind>::pointer;
+  static_assert(is_pointer_v<__rebind_pointer>);
+
+  _LIBCPP_HIDE_FROM_ABI static void* __allocate(__rebind __al, const size_t __size) {
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      return __al.allocate(static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, and the alignment of the
+      // stateful allocator
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr          = __al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      std::construct_at(__al_ptr, std::move(__al));
+      return __ptr;
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
+    requires default_initializable<_Allocator>
+  {
+    return __allocate({}, __size);
+  }
+
+  template <class _Allocator2, class... _Args>
+    requires convertible_to<const _Allocator2&, _Allocator>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(const size_t __size, allocator_arg_t, const _Allocator2& __alloc, const _Args&...) {
+    return __allocate(static_cast<__rebind>(static_cast<_Allocator>(__alloc)), __size);
+  }
+
+  template <class _This, class _Allocator2, class... _Args>
+    requires convertible_to<const _Allocator2&, _Allocator>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(const size_t __size, const _This&, allocator_arg_t, const _Allocator2& __alloc, const _Args&...) {
+    return __allocate(static_cast<__rebind>(static_cast<_Allocator>(__alloc)), __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, const size_t __size) noexcept {
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      __rebind __al;
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      __rebind* const __al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      __rebind __al = std::move(*__al_ptr);
+      std::destroy_at(__al_ptr);
+
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
+      __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+};
+
+template <>
+class __gen_promise_allocator<void> {
+private:
+  using __deallocate_fn = void (*)(void*, size_t) noexcept;
+
+  template <class _Rebind>
+  _LIBCPP_HIDE_FROM_ABI static void __deallocate(void* const __ptr, const size_t __size) noexcept {
+    using __rebind_size_type = allocator_traits<_Rebind>::size_type;
+
+    if constexpr (__gen_stateless_allocator<_Rebind>) {
+      _Rebind __al;
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__deallocate_fn));
+      __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      _Rebind* const __al_ptr = reinterpret_cast<_Rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__deallocate_fn) + alignof(_Rebind) - 1) &
+          ~(alignof(_Rebind) - 1));
+      _Rebind __al = std::move(*__al_ptr);
+      std::destroy_at(__al_ptr);
+
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__deallocate_fn) + sizeof(_Rebind) + alignof(_Rebind));
+      __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+
+  template <class _Allocator>
+  _LIBCPP_HIDE_FROM_ABI static void* __allocate(const _Allocator& __al, const size_t __size) {
+    using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
+    using __rebind_size_type = allocator_traits<__rebind>::size_type;
+
+    using __rebind_pointer = allocator_traits<__rebind>::pointer;
+    static_assert(is_pointer_v<__rebind_pointer>);
+
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__deallocate_fn));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __deallocate_fn* const __dealloc_ptr =
+          reinterpret_cast<__deallocate_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr = __deallocate<__rebind>;
+      return __ptr;
+    } else {
+      // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
+      // stateful allocator, and the deallocation function
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__deallocate_fn) + sizeof(__rebind) + alignof(__rebind));
+      void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the deallocation function in the allocated block
+      __deallocate_fn* const __dealloc_ptr =
+          reinterpret_cast<__deallocate_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr = __deallocate<__rebind>;
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__deallocate_fn) + __size + alignof(__rebind) - 1) &
+          ~(alignof(__rebind) - 1));
+      std::construct_at(__al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(const size_t __size) {
+    void* const __ptr         = ::operator new(__size + sizeof(__deallocate_fn));
+    __deallocate_fn __dealloc = [](void* const __ptr, const size_t __size) static noexcept -> void {
+      ::operator delete(__ptr, __size + sizeof(__deallocate_fn));
+    };
+
+    __deallocate_fn* const __dealloc_ptr =
+        reinterpret_cast<__deallocate_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    *__dealloc_ptr = __dealloc;
+    return __ptr;
+  }
+
+  template <class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(const size_t __size, allocator_arg_t, const _Allocator& __alloc, const _Args&...) {
+    return __allocate(__alloc, __size);
+  }
+
+  template <class _This, class _Allocator, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI void*
+  operator new(const size_t __size, const _This&, allocator_arg_t, const _Allocator& __alloc, const _Args&...) {
+    return __allocate(__alloc, __size);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, const size_t __size) noexcept {
+    __deallocate_fn* const __dealloc_ptr =
+        reinterpret_cast<__deallocate_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    const __deallocate_fn __dealloc = *__dealloc_ptr;
+    __dealloc(__ptr, __size);
+  }
+};
+
 template <class _Ref, class _Val>
 class __gen_iter {
 private:
@@ -347,7 +542,7 @@ private:
 public:
   using yielded = __gen_yielded<_Ref, _Val>;
 
-  struct promise_type : __gen_promise_base<yielded> {
+  struct promise_type : __gen_promise_base<yielded>, __gen_promise_allocator<_Allocator> {
     [[nodiscard]] _LIBCPP_HIDE_FROM_ABI generator get_return_object() noexcept {
       return generator{coroutine_handle<promise_type>::from_promise(*this)};
     }
@@ -383,6 +578,13 @@ private:
   coroutine_handle<promise_type> __coroutine_;
 };
 
+namespace pmr {
+
+template <class _Ref, class _Val = void>
+using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
+
+} // namespace pmr
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_STD_VER >= 23
diff --git a/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
new file mode 100644
index 0000000000000..56f60f60f6dfe
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
@@ -0,0 +1,123 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <cstddef>
+#include <memory>
+#include <memory_resource>
+#include <ranges>
+#include <utility>
+#include <vector>
+
+template <class T>
+class stateless_allocator {
+public:
+  using value_type = T;
+
+  stateless_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateless_allocator(const stateless_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateless_allocator<U>&) const noexcept {
+    return true;
+  }
+};
+
+template <class T>
+class stateful_allocator {
+public:
+  using value_type = T;
+
+  stateful_allocator() noexcept = default;
+
+  template <typename U>
+  constexpr stateful_allocator(const stateful_allocator<U>&) noexcept {}
+
+  [[nodiscard]] T* allocate(size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
+
+  void deallocate(void* p, size_t) noexcept { ::operator delete(p); }
+
+  template <class U>
+  constexpr bool operator==(const stateful_allocator<U>& other) const noexcept {
+    return state_ == other.state_;
+  }
+
+private:
+  int state_ = 0;
+};
+
+template <class Allocator>
+bool test_with_allocator() {
+  std::vector<int> expected_fib_vec = {0, 1, 1, 2, 3};
+  {
+    auto fib = []() -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib() | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int, int, Allocator> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+
+  {
+    auto fib = [](std::allocator_arg_t, Allocator) -> std::generator<int> {
+      int a = 0;
+      int b = 1;
+      while (true) {
+        co_yield std::exchange(a, std::exchange(b, a + b));
+      }
+    };
+    assert((fib(std::allocator_arg, {}) | std::views::take(5) | std::ranges::to<std::vector>()) == expected_fib_vec);
+  }
+  return true;
+}
+
+bool test() {
+  test_with_allocator<std::allocator<std::byte>>();
+  test_with_allocator<stateless_allocator<std::byte>>();
+  test_with_allocator<stateful_allocator<std::byte>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<std::byte>>();
+
+  test_with_allocator<std::allocator<float>>();
+  test_with_allocator<stateless_allocator<float>>();
+  test_with_allocator<stateful_allocator<float>>();
+  test_with_allocator<std::pmr::polymorphic_allocator<float>>();
+  return true;
+};
+
+int main() {
+  test();
+  return 0;
+}

>From cba0548fde3828de252106f75dd6f1018f30ce90 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Fri, 17 May 2024 22:59:21 -0400
Subject: [PATCH 07/10] [libc++] implement 'std::generator'

---
 libcxx/include/generator         | 3 +++
 libcxx/modules/std/generator.inc | 4 ++++
 2 files changed, 7 insertions(+)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 1931a4c9bdf85..6e1253d9b2e25 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -52,6 +52,7 @@ namespace std {
 #include <__type_traits/conditional.h>
 #include <__type_traits/is_nothrow_constructible.h>
 #include <__type_traits/is_object.h>
+#include <__type_traits/is_pointer.h>
 #include <__type_traits/is_reference.h>
 #include <__type_traits/is_void.h>
 #include <__type_traits/remove_cvref.h>
@@ -59,6 +60,8 @@ namespace std {
 #include <__utility/exchange.h>
 #include <__utility/move.h>
 #include <__utility/swap.h>
+#include <cstdint>
+#include <new>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
diff --git a/libcxx/modules/std/generator.inc b/libcxx/modules/std/generator.inc
index f7102b138b078..8681f4b26dc46 100644
--- a/libcxx/modules/std/generator.inc
+++ b/libcxx/modules/std/generator.inc
@@ -11,5 +11,9 @@ export namespace std {
 #if _LIBCPP_STD_VER >= 23
   // [coro.generator], class template generator
   using std::generator;
+
+  namespace pmr {
+    using std::pmr::generator;
+  }
 #endif
 } // namespace std

>From 66280fbaee5501ca779e4f6a0e9c6995b33cafe7 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Fri, 17 May 2024 23:49:29 -0400
Subject: [PATCH 08/10] [libc++] implement 'std::generator'

---
 libcxx/include/generator                      | 66 +++++++++----------
 .../test/libcxx/transitive_includes/cxx03.csv |  1 +
 .../test/libcxx/transitive_includes/cxx11.csv |  1 +
 .../test/libcxx/transitive_includes/cxx14.csv |  1 +
 .../test/libcxx/transitive_includes/cxx17.csv |  1 +
 .../test/libcxx/transitive_includes/cxx20.csv |  1 +
 .../test/libcxx/transitive_includes/cxx23.csv |  1 +
 .../test/libcxx/transitive_includes/cxx26.csv |  1 +
 8 files changed, 40 insertions(+), 33 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 6e1253d9b2e25..601998a90b887 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -328,7 +328,7 @@ private:
   using __rebind_pointer = allocator_traits<__rebind>::pointer;
   static_assert(is_pointer_v<__rebind_pointer>);
 
-  _LIBCPP_HIDE_FROM_ABI static void* __allocate(__rebind __al, const size_t __size) {
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(__rebind __al, const size_t __size) {
     if constexpr (__gen_stateless_allocator<__rebind>) {
       const size_t __block_count = __gen_aligned_block::__count(__size);
       return __al.allocate(static_cast<__rebind_size_type>(__block_count));
@@ -350,21 +350,21 @@ public:
   _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
     requires default_initializable<_Allocator>
   {
-    return __allocate({}, __size);
+    return __alloc({}, __size);
   }
 
   template <class _Allocator2, class... _Args>
     requires convertible_to<const _Allocator2&, _Allocator>
   _LIBCPP_HIDE_FROM_ABI void*
-  operator new(const size_t __size, allocator_arg_t, const _Allocator2& __alloc, const _Args&...) {
-    return __allocate(static_cast<__rebind>(static_cast<_Allocator>(__alloc)), __size);
+  operator new(const size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<__rebind>(static_cast<_Allocator>(__al)), __size);
   }
 
   template <class _This, class _Allocator2, class... _Args>
     requires convertible_to<const _Allocator2&, _Allocator>
   _LIBCPP_HIDE_FROM_ABI void*
-  operator new(const size_t __size, const _This&, allocator_arg_t, const _Allocator2& __alloc, const _Args&...) {
-    return __allocate(static_cast<__rebind>(static_cast<_Allocator>(__alloc)), __size);
+  operator new(const size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<__rebind>(static_cast<_Allocator>(__al)), __size);
   }
 
   _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, const size_t __size) noexcept {
@@ -388,32 +388,32 @@ public:
 template <>
 class __gen_promise_allocator<void> {
 private:
-  using __deallocate_fn = void (*)(void*, size_t) noexcept;
+  using __dealloc_fn = void (*)(void*, size_t) noexcept;
 
   template <class _Rebind>
-  _LIBCPP_HIDE_FROM_ABI static void __deallocate(void* const __ptr, const size_t __size) noexcept {
+  _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, const size_t __size) noexcept {
     using __rebind_size_type = allocator_traits<_Rebind>::size_type;
 
     if constexpr (__gen_stateless_allocator<_Rebind>) {
       _Rebind __al;
-      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__deallocate_fn));
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
       __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
     } else {
       // Retrieves the stateful allocator stored in the allocated block
       _Rebind* const __al_ptr = reinterpret_cast<_Rebind*>(
-          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__deallocate_fn) + alignof(_Rebind) - 1) &
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
           ~(alignof(_Rebind) - 1));
       _Rebind __al = std::move(*__al_ptr);
       std::destroy_at(__al_ptr);
 
       const size_t __block_count =
-          __gen_aligned_block::__count(__size + sizeof(__deallocate_fn) + sizeof(_Rebind) + alignof(_Rebind));
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
       __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
     }
   }
 
   template <class _Allocator>
-  _LIBCPP_HIDE_FROM_ABI static void* __allocate(const _Allocator& __al, const size_t __size) {
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, const size_t __size) {
     using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
     using __rebind_size_type = allocator_traits<__rebind>::size_type;
 
@@ -422,29 +422,29 @@ private:
 
     __rebind __rebind_al = static_cast<__rebind>(__al);
     if constexpr (__gen_stateless_allocator<__rebind>) {
-      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__deallocate_fn));
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
       void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
 
       // Stores the deallocation function in the allocated block
-      __deallocate_fn* const __dealloc_ptr =
-          reinterpret_cast<__deallocate_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
-      *__dealloc_ptr = __deallocate<__rebind>;
+      __dealloc_fn* const __dealloc_ptr =
+          reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr = __dealloc<__rebind>;
       return __ptr;
     } else {
       // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
       // stateful allocator, and the deallocation function
       const size_t __block_count =
-          __gen_aligned_block::__count(__size + sizeof(__deallocate_fn) + sizeof(__rebind) + alignof(__rebind));
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(__rebind) + alignof(__rebind));
       void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
 
       // Stores the deallocation function in the allocated block
-      __deallocate_fn* const __dealloc_ptr =
-          reinterpret_cast<__deallocate_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
-      *__dealloc_ptr = __deallocate<__rebind>;
+      __dealloc_fn* const __dealloc_ptr =
+          reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr = __dealloc<__rebind>;
 
       // Stores the stateful allocator in the allocated block
       __rebind* const __al_ptr = reinterpret_cast<__rebind*>(
-          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__deallocate_fn) + __size + alignof(__rebind) - 1) &
+          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
           ~(alignof(__rebind) - 1));
       std::construct_at(__al_ptr, std::move(__rebind_al));
       return __ptr;
@@ -453,33 +453,33 @@ private:
 
 public:
   _LIBCPP_HIDE_FROM_ABI void* operator new(const size_t __size) {
-    void* const __ptr         = ::operator new(__size + sizeof(__deallocate_fn));
-    __deallocate_fn __dealloc = [](void* const __ptr, const size_t __size) static noexcept -> void {
-      ::operator delete(__ptr, __size + sizeof(__deallocate_fn));
+    void* const __ptr         = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc = [](void* const __ptr, const size_t __size) static noexcept -> void {
+      ::operator delete(__ptr, __size + sizeof(__dealloc_fn));
     };
 
-    __deallocate_fn* const __dealloc_ptr =
-        reinterpret_cast<__deallocate_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    __dealloc_fn* const __dealloc_ptr =
+        reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
     *__dealloc_ptr = __dealloc;
     return __ptr;
   }
 
   template <class _Allocator, class... _Args>
   _LIBCPP_HIDE_FROM_ABI void*
-  operator new(const size_t __size, allocator_arg_t, const _Allocator& __alloc, const _Args&...) {
-    return __allocate(__alloc, __size);
+  operator new(const size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
   }
 
   template <class _This, class _Allocator, class... _Args>
   _LIBCPP_HIDE_FROM_ABI void*
-  operator new(const size_t __size, const _This&, allocator_arg_t, const _Allocator& __alloc, const _Args&...) {
-    return __allocate(__alloc, __size);
+  operator new(const size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+    return __alloc(__al, __size);
   }
 
   _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, const size_t __size) noexcept {
-    __deallocate_fn* const __dealloc_ptr =
-        reinterpret_cast<__deallocate_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
-    const __deallocate_fn __dealloc = *__dealloc_ptr;
+    __dealloc_fn* const __dealloc_ptr =
+        reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    const __dealloc_fn __dealloc = *__dealloc_ptr;
     __dealloc(__ptr, __size);
   }
 };
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index 913eebb64f269..047867e9279d6 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -378,6 +378,7 @@ generator cstring
 generator initializer_list
 generator limits
 generator new
+generator tuple
 generator typeinfo
 initializer_list cstddef
 iomanip istream
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index f205bdc232479..dc581f3daec0c 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -380,6 +380,7 @@ generator cstring
 generator initializer_list
 generator limits
 generator new
+generator tuple
 generator typeinfo
 initializer_list cstddef
 iomanip istream
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 85237f65959d0..58924b85ebd2e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -383,6 +383,7 @@ generator cstring
 generator initializer_list
 generator limits
 generator new
+generator tuple
 generator typeinfo
 initializer_list cstddef
 iomanip istream
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 85237f65959d0..58924b85ebd2e 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -383,6 +383,7 @@ generator cstring
 generator initializer_list
 generator limits
 generator new
+generator tuple
 generator typeinfo
 initializer_list cstddef
 iomanip istream
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 505e1363d3f53..90d8de646d2b0 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -394,6 +394,7 @@ generator cstring
 generator initializer_list
 generator limits
 generator new
+generator tuple
 generator typeinfo
 initializer_list cstddef
 iomanip istream
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 5af8dc20b70eb..1ad2522063597 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -267,6 +267,7 @@ generator cstring
 generator initializer_list
 generator limits
 generator new
+generator tuple
 generator typeinfo
 initializer_list cstddef
 iomanip istream
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 103e9487ec0c2..53053a12c9bd5 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -290,6 +290,7 @@ generator cstring
 generator initializer_list
 generator limits
 generator new
+generator tuple
 generator typeinfo
 initializer_list cstddef
 iomanip istream

>From 0bb72da775df280008fd227e0e78adfc5e093fdc Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Sat, 18 May 2024 00:16:40 -0400
Subject: [PATCH 09/10] [libc++] implement 'std::generator'

---
 libcxx/include/generator | 26 +++++++++++---------------
 1 file changed, 11 insertions(+), 15 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index 601998a90b887..d731ac31ed769 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -426,9 +426,8 @@ private:
       void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
 
       // Stores the deallocation function in the allocated block
-      __dealloc_fn* const __dealloc_ptr =
-          reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
-      *__dealloc_ptr = __dealloc<__rebind>;
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
       return __ptr;
     } else {
       // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, the alignment of the
@@ -438,9 +437,8 @@ private:
       void* const __ptr = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
 
       // Stores the deallocation function in the allocated block
-      __dealloc_fn* const __dealloc_ptr =
-          reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
-      *__dealloc_ptr = __dealloc<__rebind>;
+      __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+      *__dealloc_ptr                    = __dealloc<__rebind>;
 
       // Stores the stateful allocator in the allocated block
       __rebind* const __al_ptr = reinterpret_cast<__rebind*>(
@@ -453,14 +451,13 @@ private:
 
 public:
   _LIBCPP_HIDE_FROM_ABI void* operator new(const size_t __size) {
-    void* const __ptr         = ::operator new(__size + sizeof(__dealloc_fn));
-    __dealloc_fn __dealloc = [](void* const __ptr, const size_t __size) static noexcept -> void {
-      ::operator delete(__ptr, __size + sizeof(__dealloc_fn));
+    void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
+      ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
     };
 
-    __dealloc_fn* const __dealloc_ptr =
-        reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
-    *__dealloc_ptr = __dealloc;
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    *__dealloc_ptr                    = __dealloc;
     return __ptr;
   }
 
@@ -477,9 +474,8 @@ public:
   }
 
   _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, const size_t __size) noexcept {
-    __dealloc_fn* const __dealloc_ptr =
-        reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
-    const __dealloc_fn __dealloc = *__dealloc_ptr;
+    __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
+    const __dealloc_fn __dealloc      = *__dealloc_ptr;
     __dealloc(__ptr, __size);
   }
 };

>From 07a7db6236b87ff770ce48dbeff3c9c0c530c722 Mon Sep 17 00:00:00 2001
From: Xiaoyang Liu <siujoeng.lau at gmail.com>
Date: Mon, 20 May 2024 11:19:08 -0400
Subject: [PATCH 10/10] [libc++] implement 'std::generator'

---
 libcxx/include/generator | 94 ++++++++++++++++++++--------------------
 1 file changed, 47 insertions(+), 47 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index d731ac31ed769..87f5c99bae8f3 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -328,20 +328,22 @@ private:
   using __rebind_pointer = allocator_traits<__rebind>::pointer;
   static_assert(is_pointer_v<__rebind_pointer>);
 
-  _LIBCPP_HIDE_FROM_ABI static void* __alloc(__rebind __al, const size_t __size) {
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
+    __rebind __rebind_al = static_cast<__rebind>(__al);
+
     if constexpr (__gen_stateless_allocator<__rebind>) {
       const size_t __block_count = __gen_aligned_block::__count(__size);
-      return __al.allocate(static_cast<__rebind_size_type>(__block_count));
+      return __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
     } else {
       // Allocates enough blocks for the coroutine frame, the size of the stateful allocator, and the alignment of the
       // stateful allocator
       const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
-      void* const __ptr          = __al.allocate(static_cast<__rebind_size_type>(__block_count));
+      void* const __ptr          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
 
       // Stores the stateful allocator in the allocated block
-      __rebind* const __al_ptr = reinterpret_cast<__rebind*>(
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
           (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
-      std::construct_at(__al_ptr, std::move(__al));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
       return __ptr;
     }
   }
@@ -355,32 +357,31 @@ public:
 
   template <class _Allocator2, class... _Args>
     requires convertible_to<const _Allocator2&, _Allocator>
-  _LIBCPP_HIDE_FROM_ABI void*
-  operator new(const size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
-    return __alloc(static_cast<__rebind>(static_cast<_Allocator>(__al)), __size);
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
   }
 
   template <class _This, class _Allocator2, class... _Args>
     requires convertible_to<const _Allocator2&, _Allocator>
   _LIBCPP_HIDE_FROM_ABI void*
-  operator new(const size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
-    return __alloc(static_cast<__rebind>(static_cast<_Allocator>(__al)), __size);
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator2& __al, const _Args&...) {
+    return __alloc(static_cast<_Allocator>(__al), __size);
   }
 
-  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, const size_t __size) noexcept {
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
     if constexpr (__gen_stateless_allocator<__rebind>) {
-      __rebind __al;
+      __rebind __rebind_al;
       const size_t __block_count = __gen_aligned_block::__count(__size);
-      __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
     } else {
       // Retrieves the stateful allocator stored in the allocated block
-      __rebind* const __al_ptr = reinterpret_cast<__rebind*>(
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
           (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
-      __rebind __al = std::move(*__al_ptr);
-      std::destroy_at(__al_ptr);
+      __rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
 
       const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__rebind) + alignof(__rebind));
-      __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
     }
   }
 };
@@ -390,30 +391,8 @@ class __gen_promise_allocator<void> {
 private:
   using __dealloc_fn = void (*)(void*, size_t) noexcept;
 
-  template <class _Rebind>
-  _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, const size_t __size) noexcept {
-    using __rebind_size_type = allocator_traits<_Rebind>::size_type;
-
-    if constexpr (__gen_stateless_allocator<_Rebind>) {
-      _Rebind __al;
-      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
-      __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
-    } else {
-      // Retrieves the stateful allocator stored in the allocated block
-      _Rebind* const __al_ptr = reinterpret_cast<_Rebind*>(
-          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
-          ~(alignof(_Rebind) - 1));
-      _Rebind __al = std::move(*__al_ptr);
-      std::destroy_at(__al_ptr);
-
-      const size_t __block_count =
-          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
-      __al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
-    }
-  }
-
   template <class _Allocator>
-  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, const size_t __size) {
+  _LIBCPP_HIDE_FROM_ABI static void* __alloc(const _Allocator& __al, size_t __size) {
     using __rebind           = allocator_traits<_Allocator>::template rebind_alloc<__gen_aligned_block>;
     using __rebind_size_type = allocator_traits<__rebind>::size_type;
 
@@ -441,16 +420,38 @@ private:
       *__dealloc_ptr                    = __dealloc<__rebind>;
 
       // Stores the stateful allocator in the allocated block
-      __rebind* const __al_ptr = reinterpret_cast<__rebind*>(
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
           (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
           ~(alignof(__rebind) - 1));
-      std::construct_at(__al_ptr, std::move(__rebind_al));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
       return __ptr;
     }
   }
 
+  template <class _Rebind>
+  _LIBCPP_HIDE_FROM_ABI static void __dealloc(void* const __ptr, size_t __size) noexcept {
+    using __rebind_size_type = allocator_traits<_Rebind>::size_type;
+
+    if constexpr (__gen_stateless_allocator<_Rebind>) {
+      _Rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size + sizeof(__dealloc_fn));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    } else {
+      // Retrieves the stateful allocator stored in the allocated block
+      _Rebind* const __rebind_al_ptr = reinterpret_cast<_Rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + sizeof(__dealloc_fn) + alignof(_Rebind) - 1) &
+          ~(alignof(_Rebind) - 1));
+      _Rebind __rebind_al = std::move(*__rebind_al_ptr);
+      std::destroy_at(__rebind_al_ptr);
+
+      const size_t __block_count =
+          __gen_aligned_block::__count(__size + sizeof(__dealloc_fn) + sizeof(_Rebind) + alignof(_Rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+
 public:
-  _LIBCPP_HIDE_FROM_ABI void* operator new(const size_t __size) {
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size) {
     void* const __ptr      = ::operator new(__size + sizeof(__dealloc_fn));
     __dealloc_fn __dealloc = [](void* const __dealloc_ptr, const size_t __dealloc_size) static noexcept -> void {
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
@@ -462,18 +463,17 @@ public:
   }
 
   template <class _Allocator, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI void*
-  operator new(const size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size, allocator_arg_t, const _Allocator& __al, const _Args&...) {
     return __alloc(__al, __size);
   }
 
   template <class _This, class _Allocator, class... _Args>
   _LIBCPP_HIDE_FROM_ABI void*
-  operator new(const size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
+  operator new(size_t __size, const _This&, allocator_arg_t, const _Allocator& __al, const _Args&...) {
     return __alloc(__al, __size);
   }
 
-  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, const size_t __size) noexcept {
+  _LIBCPP_HIDE_FROM_ABI static void operator delete(void* const __ptr, size_t __size) noexcept {
     __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
     const __dealloc_fn __dealloc      = *__dealloc_ptr;
     __dealloc(__ptr, __size);



More information about the libcxx-commits mailing list