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

Connector Switch via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jun 5 08:28:11 PDT 2026


https://github.com/c8ef updated https://github.com/llvm/llvm-project/pull/201854

>From 22f5fee287889d72cb171751fdc0993197c69b6b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:45:16 +0800
Subject: [PATCH 1/3] implement generator

---
 libcxx/docs/FeatureTestMacroTable.rst         |   2 +
 libcxx/docs/Status/Cxx23Issues.csv            |   2 +-
 libcxx/docs/Status/Cxx23Papers.csv            |   4 +-
 libcxx/docs/Status/Cxx2cIssues.csv            |   8 +-
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/generator                      | 603 ++++++++++++++++++
 libcxx/include/module.modulemap.in            |   5 +
 libcxx/include/version                        |   2 +
 libcxx/modules/std.compat.cppm.in             |   3 -
 libcxx/modules/std.cppm.in                    |   4 +-
 libcxx/modules/std/generator.inc              |   7 +-
 .../coro.generator/nodiscard.verify.cpp       |  29 +
 .../test/libcxx/transitive_includes/cxx03.csv |  21 +
 .../test/libcxx/transitive_includes/cxx11.csv |  21 +
 .../test/libcxx/transitive_includes/cxx14.csv |  21 +
 .../test/libcxx/transitive_includes/cxx17.csv |  21 +
 .../test/libcxx/transitive_includes/cxx20.csv |  16 +
 .../test/libcxx/transitive_includes/cxx23.csv |   9 +
 .../test/libcxx/transitive_includes/cxx26.csv |   9 +
 .../generator.version.compile.pass.cpp        |  65 ++
 .../version.version.compile.pass.cpp          |  30 +
 .../ranges/coro.generator/allocator.pass.cpp  | 123 ++++
 .../coro.generator/generator.compile.pass.cpp |  48 ++
 .../ranges/coro.generator/generator.pass.cpp  |  56 ++
 .../ranges/coro.generator/recursive.pass.cpp  |  97 +++
 .../generate_feature_test_macro_components.py |   5 +
 libcxx/utils/libcxx/header_information.py     |   1 -
 27 files changed, 1198 insertions(+), 15 deletions(-)
 create mode 100644 libcxx/include/generator
 create mode 100644 libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
 create mode 100644 libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/allocator.pass.cpp
 create mode 100644 libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
 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/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 1b748e37293df..46d83644ff10f 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -344,6 +344,8 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_forward_like``                                 ``202207L``
     ---------------------------------------------------------- -----------------
+    ``__cpp_lib_generator``                                    ``202207L``
+    ---------------------------------------------------------- -----------------
     ``__cpp_lib_invoke_r``                                     ``202106L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_ios_noreplace``                                ``202207L``
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 0a2f0d59484de..b61edbd0a2975 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -217,7 +217,7 @@
 "`LWG3759 <https://wg21.link/LWG3759>`__","``ranges::rotate_copy`` should use ``std::move``","2022-11 (Kona)","|Complete|","15","`#105027 <https://github.com/llvm/llvm-project/issues/105027>`__",""
 "`LWG3760 <https://wg21.link/LWG3760>`__","``cartesian_product_view::iterator``'s ``parent_`` is never valid","2022-11 (Kona)","","","`#105028 <https://github.com/llvm/llvm-project/issues/105028>`__",""
 "`LWG3761 <https://wg21.link/LWG3761>`__","``cartesian_product_view::iterator::operator-`` should pass by reference","2022-11 (Kona)","","","`#105029 <https://github.com/llvm/llvm-project/issues/105029>`__",""
-"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","","","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
+"`LWG3762 <https://wg21.link/LWG3762>`__","``generator::iterator::operator==`` should pass by reference","2022-11 (Kona)","|Complete|","23","`#105030 <https://github.com/llvm/llvm-project/issues/105030>`__",""
 "`LWG3764 <https://wg21.link/LWG3764>`__","``reference_wrapper::operator()`` should propagate noexcept","2022-11 (Kona)","|Complete|","17","`#105031 <https://github.com/llvm/llvm-project/issues/105031>`__",""
 "`LWG3765 <https://wg21.link/LWG3765>`__","``const_sentinel`` should be constrained","2022-11 (Kona)","","","`#105032 <https://github.com/llvm/llvm-project/issues/105032>`__",""
 "`LWG3766 <https://wg21.link/LWG3766>`__","``view_interface::cbegin`` is underconstrained","2022-11 (Kona)","","","`#105033 <https://github.com/llvm/llvm-project/issues/105033>`__",""
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index eb580ea891f5b..862461f900dbd 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -80,7 +80,7 @@
 "`P2474R2 <https://wg21.link/P2474R2>`__","``views::repeat``","2022-07 (Virtual)","|Complete|","17","`#105222 <https://github.com/llvm/llvm-project/issues/105222>`__",""
 "`P2494R2 <https://wg21.link/P2494R2>`__","Relaxing range adaptors to allow for move only types","2022-07 (Virtual)","|Complete|","17","`#105224 <https://github.com/llvm/llvm-project/issues/105224>`__",""
 "`P2499R0 <https://wg21.link/P2499R0>`__","``string_view`` range constructor should be ``explicit``","2022-07 (Virtual)","|Complete|","16","`#105225 <https://github.com/llvm/llvm-project/issues/105225>`__",""
-"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","","","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
+"`P2502R2 <https://wg21.link/P2502R2>`__","``std::generator``: Synchronous Coroutine Generator for Ranges","2022-07 (Virtual)","|Complete|","23","`#105226 <https://github.com/llvm/llvm-project/issues/105226>`__",""
 "`P2508R1 <https://wg21.link/P2508R1>`__","Exposing ``std::basic-format-string``","2022-07 (Virtual)","|Complete|","15","`#105227 <https://github.com/llvm/llvm-project/issues/105227>`__",""
 "`P2517R1 <https://wg21.link/P2517R1>`__","Add a conditional ``noexcept`` specification to ``std::apply``","2022-07 (Virtual)","|Complete|","3.9","`#105229 <https://github.com/llvm/llvm-project/issues/105229>`__",""
 "`P2520R0 <https://wg21.link/P2520R0>`__","``move_iterator`` should be a random access iterator","2022-07 (Virtual)","|Complete|","17","`#105230 <https://github.com/llvm/llvm-project/issues/105230>`__","Implemented as a DR in C++20"
@@ -115,7 +115,7 @@
 "`P2674R1 <https://wg21.link/P2674R1>`__","A trait for implicit lifetime types","2023-02 (Issaquah)","|Complete|","20","`#105259 <https://github.com/llvm/llvm-project/issues/105259>`__",""
 "`P2655R3 <https://wg21.link/P2655R3>`__","``common_reference_t`` of ``reference_wrapper`` Should Be a Reference Type","2023-02 (Issaquah)","|Complete|","21","`#105260 <https://github.com/llvm/llvm-project/issues/105260>`__","The paper is implemented as a DR to C++20"
 "`P2652R2 <https://wg21.link/P2652R2>`__","Disallow User Specialization of ``allocator_traits``","2023-02 (Issaquah)","|Complete|","19","`#105262 <https://github.com/llvm/llvm-project/issues/105262>`__",""
-"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","","","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
+"`P2787R1 <https://wg21.link/P2787R1>`__","``pmr::generator`` - Promise Types are not Values","2023-02 (Issaquah)","|Complete|","23","`#105263 <https://github.com/llvm/llvm-project/issues/105263>`__",""
 "`P2614R2 <https://wg21.link/P2614R2>`__","Deprecate ``numeric_limits::has_denorm``","2023-02 (Issaquah)","|Complete|","18","`#105264 <https://github.com/llvm/llvm-project/issues/105264>`__",""
 "`P2588R3 <https://wg21.link/P2588R3>`__","``barrier``’s phase completion guarantees","2023-02 (Issaquah)","","","`#105265 <https://github.com/llvm/llvm-project/issues/105265>`__",""
 "`P2763R1 <https://wg21.link/P2763R1>`__","``layout_stride`` static extents default constructor fix","2023-02 (Issaquah)","","","`#105266 <https://github.com/llvm/llvm-project/issues/105266>`__",""
diff --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index 6a836c0491bb6..3ec8ae821ed5d 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -4,7 +4,7 @@
 "`LWG3885 <https://wg21.link/LWG3885>`__","``op`` should be in [zombie.names]","2023-06 (Varna)","|Nothing To Do|","","`#105270 <https://github.com/llvm/llvm-project/issues/105270>`__",""
 "`LWG3887 <https://wg21.link/LWG3887>`__","Version macro for ``allocate_at_least``","2023-06 (Varna)","|Complete|","19","`#105271 <https://github.com/llvm/llvm-project/issues/105271>`__",""
 "`LWG3893 <https://wg21.link/LWG3893>`__","LWG 3661 broke ``atomic<shared_ptr<T>> a; a = nullptr;``","2023-06 (Varna)","","","`#105273 <https://github.com/llvm/llvm-project/issues/105273>`__",""
-"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","","","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
+"`LWG3894 <https://wg21.link/LWG3894>`__","``generator::promise_type::yield_value(ranges::elements_of<Rng, Alloc>)`` should not be ``noexcept``","2023-06 (Varna)","|Complete|","23","`#105274 <https://github.com/llvm/llvm-project/issues/105274>`__",""
 "`LWG3903 <https://wg21.link/LWG3903>`__","span destructor is redundantly noexcept","2023-06 (Varna)","|Complete|","7","`#105275 <https://github.com/llvm/llvm-project/issues/105275>`__",""
 "`LWG3904 <https://wg21.link/LWG3904>`__","``lazy_split_view::outer-iterator``'s const-converting constructor isn't setting ``trailing_empty_``","2023-06 (Varna)","","","`#105276 <https://github.com/llvm/llvm-project/issues/105276>`__",""
 "`LWG3905 <https://wg21.link/LWG3905>`__","Type of ``std::fexcept_t``","2023-06 (Varna)","|Complete|","3.4","`#105277 <https://github.com/llvm/llvm-project/issues/105277>`__",""
@@ -79,8 +79,8 @@
 "`LWG3216 <https://wg21.link/LWG3216>`__","Rebinding the allocator before calling ``construct``/``destroy`` in ``allocate_shared``","2024-11 (Wrocław)","","","`#118332 <https://github.com/llvm/llvm-project/issues/118332>`__",""
 "`LWG3436 <https://wg21.link/LWG3436>`__","``std::construct_at`` should support arrays","2024-11 (Wrocław)","","","`#118335 <https://github.com/llvm/llvm-project/issues/118335>`__",""
 "`LWG3886 <https://wg21.link/LWG3886>`__","Monad mo' problems","2024-11 (Wrocław)","|Complete|","22","`#118336 <https://github.com/llvm/llvm-project/issues/118336>`__",""
-"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","","","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
-"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","","","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
+"`LWG3899 <https://wg21.link/LWG3899>`__","``co_yield``\ing elements of an lvalue generator is unnecessarily inefficient","2024-11 (Wrocław)","|Complete|","23","`#118337 <https://github.com/llvm/llvm-project/issues/118337>`__",""
+"`LWG3900 <https://wg21.link/LWG3900>`__","The ``allocator_arg_t`` overloads of ``generator::promise_type::operator new`` should not be constrained","2024-11 (Wrocław)","|Complete|","23","`#118338 <https://github.com/llvm/llvm-project/issues/118338>`__",""
 "`LWG3918 <https://wg21.link/LWG3918>`__","``std::uninitialized_move/_n`` and guaranteed copy elision","2024-11 (Wrocław)","","","`#118339 <https://github.com/llvm/llvm-project/issues/118339>`__",""
 "`LWG4014 <https://wg21.link/LWG4014>`__","LWG 3809 changes behavior of some existing ``std::subtract_with_carry_engine code``","2024-11 (Wrocław)","","","`#118340 <https://github.com/llvm/llvm-project/issues/118340>`__",""
 "`LWG4024 <https://wg21.link/LWG4024>`__","Underspecified destruction of objects created in ``std::make_shared_for_overwrite``/``std::allocate_shared_for_overwrite``","2024-11 (Wrocław)","|Complete|","16","`#118341 <https://github.com/llvm/llvm-project/issues/118341>`__",""
@@ -93,7 +93,7 @@
 "`LWG4088 <https://wg21.link/LWG4088>`__","``println`` ignores the locale imbued in ``std::ostream``","2024-11 (Wrocław)","|Complete|","18","`#118348 <https://github.com/llvm/llvm-project/issues/118348>`__",""
 "`LWG4112 <https://wg21.link/LWG4112>`__","``has-arrow`` should required ``operator->()`` to be ``const``-qualified","2024-11 (Wrocław)","","","`#118349 <https://github.com/llvm/llvm-project/issues/118349>`__",""
 "`LWG4113 <https://wg21.link/LWG4113>`__","Disallow ``has_unique_object_representations<Incomplete[]>``","2024-11 (Wrocław)","|Complete|","","`#118350 <https://github.com/llvm/llvm-project/issues/118350>`__",""
-"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","","","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
+"`LWG4119 <https://wg21.link/LWG4119>`__","``generator::promise_type::yield_value(ranges::elements_of<R, Alloc>)``'s nested ``generator`` may be ill-formed","2024-11 (Wrocław)","|Complete|","23","`#118351 <https://github.com/llvm/llvm-project/issues/118351>`__",""
 "`LWG4124 <https://wg21.link/LWG4124>`__","Cannot format ``zoned_time`` with resolution coarser than ``seconds``","2024-11 (Wrocław)","","","`#118352 <https://github.com/llvm/llvm-project/issues/118352>`__",""
 "`LWG4126 <https://wg21.link/LWG4126>`__","Some feature-test macros for fully freestanding features are not yet marked freestanding","2024-11 (Wrocław)","","","`#118353 <https://github.com/llvm/llvm-project/issues/118353>`__",""
 "`LWG4134 <https://wg21.link/LWG4134>`__","Issue with Philox algorithm specification","2024-11 (Wrocław)","","","`#118354 <https://github.com/llvm/llvm-project/issues/118354>`__",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 10dfb4b4d58cb..cd77d51924571 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -1043,6 +1043,7 @@ set(files
   fstream
   functional
   future
+  generator
   initializer_list
   iomanip
   ios
diff --git a/libcxx/include/generator b/libcxx/include/generator
new file mode 100644
index 0000000000000..d3f942b089002
--- /dev/null
+++ b/libcxx/include/generator
@@ -0,0 +1,603 @@
+// -*- 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
+
+/*
+
+namespace std {
+  // [coro.generator.class], class template generator
+  template<class Ref, class Val = void, class Allocator = void>
+    class generator;
+
+  namespace pmr {
+    template<class Ref, class Val = void>
+      using generator = std::generator<Ref, Val, polymorphic_allocator<>>;
+  }
+}
+
+*/
+
+#include <__algorithm/max.h>
+#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>
+#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 <__memory/construct_at.h>
+#include <__memory_resource/polymorphic_allocator.h>
+#include <__ranges/access.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_pointer.h>
+#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>
+#include <cstdint>
+#include <new>
+
+#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
+
+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>
+struct __gen_iter_provider {
+  class __iterator;
+};
+
+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 struct __gen_iter_provider;
+
+  // 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.
+    coroutine_handle<__gen_promise_base> __active;
+  };
+
+  struct __recursive_data {
+    exception_ptr __exception;
+    coroutine_handle<__gen_promise_base> __parent;
+    coroutine_handle<__gen_promise_base> __root;
+  };
+
+  union __union {
+    __root_data __root;
+    __recursive_data __recursive;
+
+    _LIBCPP_HIDE_FROM_ABI ~__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 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 {
+    _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));
+      }
+    }
+
+  private:
+    generator<_Ref, _Val, _Allocator> __gen_;
+  };
+
+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) 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}};
+  }
+
+  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 <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, void, _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() {
+#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+    if (__is_root()) {
+      throw;
+    } else {
+      __get_recursive_data().__exception = std::current_exception();
+    }
+#  endif
+  }
+};
+
+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* __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 __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          = __rebind_al.allocate(static_cast<__rebind_size_type>(__block_count));
+
+      // Stores the stateful allocator in the allocated block
+      __rebind* const __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + __size + alignof(__rebind) - 1) & ~(alignof(__rebind) - 1));
+      std::construct_at(__rebind_al_ptr, std::move(__rebind_al));
+      return __ptr;
+    }
+  }
+
+public:
+  _LIBCPP_HIDE_FROM_ABI void* operator new(size_t __size)
+    requires default_initializable<_Allocator>
+  {
+    return __alloc({}, __size);
+  }
+
+  template <class _Allocator2, class... _Args>
+  _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>
+  _LIBCPP_HIDE_FROM_ABI void*
+  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, size_t __size) noexcept {
+    if constexpr (__gen_stateless_allocator<__rebind>) {
+      __rebind __rebind_al;
+      const size_t __block_count = __gen_aligned_block::__count(__size);
+      __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 + 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(__rebind) + alignof(__rebind));
+      __rebind_al.deallocate(static_cast<__gen_aligned_block*>(__ptr), static_cast<__rebind_size_type>(__block_count));
+    }
+  }
+};
+
+template <>
+class __gen_promise_allocator<void> {
+private:
+  using __dealloc_fn = void (*)(void*, size_t) noexcept;
+
+  template <class _Allocator>
+  _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;
+
+    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(__dealloc_fn));
+      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>;
+      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(__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
+      __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 __rebind_al_ptr = reinterpret_cast<__rebind*>(
+          (reinterpret_cast<uintptr_t>(__ptr) + sizeof(__dealloc_fn) + __size + alignof(__rebind) - 1) &
+          ~(alignof(__rebind) - 1));
+      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(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));
+    };
+
+    __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(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(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, 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);
+  }
+};
+
+template <class _Ref, class _Val>
+class __gen_iter_provider<_Ref, _Val>::__iterator {
+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 __iterator(
+      coroutine_handle<__gen_promise_base<__gen_yielded<_Ref, _Val>>> __coroutine) noexcept
+      : __coroutine_{__coroutine} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator(__iterator&& __other) noexcept
+      : __coroutine_{std::exchange(__other.__coroutine_, {})} {}
+
+  _LIBCPP_HIDE_FROM_ABI __iterator& operator=(__iterator&& __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 __iterator& operator++() {
+    __coroutine_.promise().__active().resume();
+    return *this;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI void operator++(int) { ++*this; }
+
+  [[nodiscard]] _LIBCPP_HIDE_FROM_ABI friend bool operator==(const __iterator& __iter, default_sentinel_t) noexcept {
+    return __iter.__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>, __gen_promise_allocator<_Allocator> {
+    [[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_provider<_Ref, _Val>::__iterator begin() {
+    auto __coroutine = coroutine_handle<__gen_promise_base<yielded>>::from_promise(__coroutine_.promise());
+    __coroutine.resume();
+    return typename __gen_iter_provider<_Ref, _Val>::__iterator{__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_;
+};
+
+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
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP_GENERATOR
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 29f3818c39671..efb97a047c6e6 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1476,6 +1476,11 @@ module std [system] {
     export *
   }
 
+  module generator {
+    header "generator"
+    export *
+  }
+
   module iomanip {
     header "iomanip"
     export *
diff --git a/libcxx/include/version b/libcxx/include/version
index d4d29b4d31a72..8a73c95551793 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -133,6 +133,7 @@ __cpp_lib_fstream_native_handle                         202306L <fstream>
 __cpp_lib_function_ref                                  202306L <functional>
 __cpp_lib_gcd_lcm                                       201606L <numeric>
 __cpp_lib_generate_random                               202403L <random>
+__cpp_lib_generator                                     202207L <generator>
 __cpp_lib_generic_associative_lookup                    201304L <map> <set>
 __cpp_lib_generic_unordered_lookup                      201811L <unordered_map> <unordered_set>
 __cpp_lib_hardware_interference_size                    201703L <new>
@@ -507,6 +508,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_format_ranges                        202207L
 // # define __cpp_lib_formatters                           202302L
 # define __cpp_lib_forward_like                         202207L
+# define __cpp_lib_generator                            202207L
 # define __cpp_lib_invoke_r                             202106L
 # define __cpp_lib_ios_noreplace                        202207L
 # if __has_builtin(__builtin_is_implicit_lifetime)
diff --git a/libcxx/modules/std.compat.cppm.in b/libcxx/modules/std.compat.cppm.in
index dd7385bf33a42..dada8f56f0224 100644
--- a/libcxx/modules/std.compat.cppm.in
+++ b/libcxx/modules/std.compat.cppm.in
@@ -51,9 +51,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  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 984b18321923c..0c484219c31ce 100644
--- a/libcxx/modules/std.cppm.in
+++ b/libcxx/modules/std.cppm.in
@@ -67,6 +67,7 @@ module;
 #include <fstream>
 #include <functional>
 #include <future>
+#include <generator>
 #include <initializer_list>
 #include <iomanip>
 #include <ios>
@@ -135,9 +136,6 @@ module;
 #  if __has_include(<debugging>)
 #    error "please update the header information for <debugging> in headers_not_available in utils/libcxx/header_information.py"
 #  endif // __has_include(<debugging>)
-#  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..8681f4b26dc46 100644
--- a/libcxx/modules/std/generator.inc
+++ b/libcxx/modules/std/generator.inc
@@ -8,7 +8,12 @@
 //===----------------------------------------------------------------------===//
 
 export namespace std {
-#if 0
+#if _LIBCPP_STD_VER >= 23
+  // [coro.generator], class template generator
   using std::generator;
+
+  namespace pmr {
+    using std::pmr::generator;
+  }
 #endif
 } // namespace std
diff --git a/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
new file mode 100644
index 0000000000000..9fb717bf9c148
--- /dev/null
+++ b/libcxx/test/libcxx/ranges/coro.generator/nodiscard.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// Check that functions are marked [[nodiscard]]
+
+#include <generator>
+
+std::generator<int> generate_ints() {
+  co_yield 1;
+  co_yield 2;
+  co_yield 3;
+}
+
+void test() {
+  auto gen = generate_ints();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.begin();
+
+  // expected-warning at +1 {{ignoring return value of function declared with 'nodiscard' attribute}}
+  gen.end();
+}
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..5754f5961dd09 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -945,6 +949,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2032,6 +2052,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..c31b86c5f8739 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -125,15 +125,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -968,6 +972,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2072,6 +2092,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..50f2d9cf2ac5f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -122,15 +122,19 @@ atomic ratio
 atomic type_traits
 atomic version
 barrier atomic
+barrier cctype
 barrier climits
 barrier cmath
 barrier compare
 barrier concepts
 barrier cstddef
 barrier cstdint
+barrier cstdio
 barrier cstdlib
 barrier cstring
 barrier ctime
+barrier cwchar
+barrier cwctype
 barrier exception
 barrier initializer_list
 barrier iosfwd
@@ -971,6 +975,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
@@ -2085,6 +2105,7 @@ stdexcept new
 stdexcept type_traits
 stdexcept typeinfo
 stdexcept version
+stop_token cstddef
 stop_token iosfwd
 stop_token version
 streambuf algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 55c79acff5a8f..0d23c92ec9a1d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -965,6 +965,22 @@ future utility
 future variant
 future vector
 future version
+generator cmath
+generator compare
+generator cstddef
+generator cstdint
+generator cstdlib
+generator cstring
+generator exception
+generator initializer_list
+generator iosfwd
+generator limits
+generator new
+generator tuple
+generator type_traits
+generator typeinfo
+generator utility
+generator version
 initializer_list cstddef
 initializer_list version
 iomanip algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 073f698786117..2fde541964065 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -466,6 +466,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d531d60ae3e35..5fde2907b3327 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -443,6 +443,15 @@ future string_view
 future tuple
 future typeinfo
 future version
+generator compare
+generator cstdint
+generator cstring
+generator initializer_list
+generator limits
+generator new
+generator tuple
+generator typeinfo
+generator version
 initializer_list version
 iomanip cctype
 iomanip cerrno
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
new file mode 100644
index 0000000000000..0726f4e59a553
--- /dev/null
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/generator.version.compile.pass.cpp
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// WARNING: This test was generated by generate_feature_test_macro_components.py
+// and should not be edited manually.
+
+// <generator>
+
+// Test the feature test macros defined by <generator>
+
+// clang-format off
+
+#include <generator>
+#include "test_macros.h"
+
+#if TEST_STD_VER < 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 14
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 17
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 20
+
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
+#elif TEST_STD_VER == 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
+#elif TEST_STD_VER > 23
+
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
+#endif // TEST_STD_VER > 23
+
+// clang-format on
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 20c16700ef76a..32befec34ed09 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -412,6 +412,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifdef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should not be defined before c++14"
 #  endif
@@ -1351,6 +1355,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++14"
 #  endif
@@ -2413,6 +2421,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++17"
 #  endif
@@ -3742,6 +3754,10 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifdef __cpp_lib_generator
+#    error "__cpp_lib_generator should not be defined before c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++20"
 #  endif
@@ -5266,6 +5282,13 @@
 #    error "__cpp_lib_generate_random should not be defined before c++26"
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++23"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++23"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++23"
 #  endif
@@ -7144,6 +7167,13 @@
 #    endif
 #  endif
 
+#  ifndef __cpp_lib_generator
+#    error "__cpp_lib_generator should be defined in c++26"
+#  endif
+#  if __cpp_lib_generator != 202207L
+#    error "__cpp_lib_generator should have the value 202207L in c++26"
+#  endif
+
 #  ifndef __cpp_lib_generic_associative_lookup
 #    error "__cpp_lib_generic_associative_lookup should be defined in c++26"
 #  endif
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..96c2211ed3138
--- /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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <generator>
+
+// template<class Ref, class Val = 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;
+}
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..769a56021c32b
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.compile.pass.cpp
@@ -0,0 +1,48 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+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
new file mode 100644
index 0000000000000..ed1793382962a
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/generator.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class 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<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 = range_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..36c5b8b65e42e
--- /dev/null
+++ b/libcxx/test/std/ranges/coro.generator/recursive.pass.cpp
@@ -0,0 +1,97 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <generator>
+
+// template<class Ref, class Val = void, class Allocator = void>
+//   class generator;
+
+#include <generator>
+
+#include <cassert>
+#include <numeric>
+#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);
+    assert(std::reduce(node_vec.cbegin(), node_vec.cend()) == 2036);
+    delete tree_root;
+  }
+  return true;
+}
+
+int main() {
+  test();
+  return 0;
+}
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index d8fb575b97b2f..a5db6f1f1650f 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -719,6 +719,11 @@ def add_version_header(tc):
             "headers": ["random"],
             "unimplemented": True,
         },
+        {
+            "name": "__cpp_lib_generator",
+            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "headers": ["generator"],
+        },
         {
             "name": "__cpp_lib_generic_associative_lookup",
             "values": {"c++14": 201304},
diff --git a/libcxx/utils/libcxx/header_information.py b/libcxx/utils/libcxx/header_information.py
index d06271a7908cc..cc33d32a69201 100644
--- a/libcxx/utils/libcxx/header_information.py
+++ b/libcxx/utils/libcxx/header_information.py
@@ -164,7 +164,6 @@ def __hash__(self) -> int:
 # modules will fail to build if a header is added but this list is not updated.
 headers_not_available = list(map(Header, [
     "debugging",
-    "generator",
     "hazard_pointer",
     "inplace_vector",
     "linalg",

>From f2ee26c784c91829799073fe12d1cbd0d281a755 Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 22:52:35 +0800
Subject: [PATCH 2/3] release notes & python format

---
 libcxx/docs/ReleaseNotes/23.rst                        | 2 ++
 libcxx/utils/generate_feature_test_macro_components.py | 4 +++-
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 03dabc54dcf79..a53fad86004a6 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -51,6 +51,8 @@ Implemented Papers
 - P2542R8: ``views::concat`` (`Github <https://llvm.org/PR105419>`__)
 - P3383R3: ``mdspan.at()`` (`Github <https://llvm.org/PR175213>`__)
 - P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
+- P2502R2: ``std::generator``: Synchronous Coroutine Generator for Ranges (`Github <https://llvm.org/PR105226>`__)
+- P2787R1: ``pmr::generator`` - Promise Types are not Values (`Github <https://llvm.org/PR105263>`__)
 
 Improvements and New Features
 -----------------------------
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index a5db6f1f1650f..ea2e43b4c15a8 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -721,7 +721,9 @@ def add_version_header(tc):
         },
         {
             "name": "__cpp_lib_generator",
-            "values": {"c++23": 202207}, # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
+            "values": {
+                "c++23": 202207
+            },  # P2502R2: std::generator: Synchronous Coroutine Generator for Ranges
             "headers": ["generator"],
         },
         {

>From 19e5608ff21db2369e7172201d55bc372e48d67b Mon Sep 17 00:00:00 2001
From: c8ef <c8ef at outlook.com>
Date: Fri, 5 Jun 2026 23:27:41 +0800
Subject: [PATCH 3/3] test

---
 libcxx/include/generator | 109 +++++++++++++++++++++------------------
 1 file changed, 60 insertions(+), 49 deletions(-)

diff --git a/libcxx/include/generator b/libcxx/include/generator
index d3f942b089002..7792b65a6ae43 100644
--- a/libcxx/include/generator
+++ b/libcxx/include/generator
@@ -25,52 +25,56 @@ namespace std {
 
 */
 
-#include <__algorithm/max.h>
-#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>
-#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 <__memory/construct_at.h>
-#include <__memory_resource/polymorphic_allocator.h>
-#include <__ranges/access.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_pointer.h>
-#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>
-#include <cstdint>
-#include <new>
-
-#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
-#  pragma GCC system_header
-#endif
+#if __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+#  include <__cxx03/__config>
+#else
+#  include <__config>
+
+#  if _LIBCPP_STD_VER >= 23
+
+#    include <__algorithm/max.h>
+#    include <__assert>
+#    include <__concepts/common_reference_with.h>
+#    include <__concepts/constructible.h>
+#    include <__concepts/convertible_to.h>
+#    include <__concepts/same_as.h>
+#    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 <__memory/construct_at.h>
+#    include <__memory_resource/polymorphic_allocator.h>
+#    include <__ranges/access.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_pointer.h>
+#    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>
+#    include <cstdint>
+#    include <new>
+
+#    if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#      pragma GCC system_header
+#    endif
 
 _LIBCPP_PUSH_MACROS
-#include <__undef_macros>
-
-#if _LIBCPP_STD_VER >= 23
+#    include <__undef_macros>
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
@@ -309,13 +313,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI void return_void() const noexcept {}
 
   _LIBCPP_HIDE_FROM_ABI void unhandled_exception() {
-#  ifndef _LIBCPP_HAS_NO_EXCEPTIONS
+#    ifndef _LIBCPP_HAS_NO_EXCEPTIONS
     if (__is_root()) {
       throw;
     } else {
       __get_recursive_data().__exception = std::current_exception();
     }
-#  endif
+#    endif
   }
 };
 
@@ -462,9 +466,14 @@ private:
 
 public:
   _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 {
+    void* const __ptr = ::operator new(__size + sizeof(__dealloc_fn));
+    __dealloc_fn __dealloc =
+        [](void* const __dealloc_ptr, [[maybe_unused]] const size_t __dealloc_size) static noexcept -> void {
+#    ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
+      ::operator delete(__dealloc_ptr);
+#    else
       ::operator delete(__dealloc_ptr, __dealloc_size + sizeof(__dealloc_fn));
+#    endif
     };
 
     __dealloc_fn* const __dealloc_ptr = reinterpret_cast<__dealloc_fn*>(reinterpret_cast<uintptr_t>(__ptr) + __size);
@@ -596,8 +605,10 @@ using generator = std::generator<_Ref, _Val, polymorphic_allocator<>>;
 
 _LIBCPP_END_NAMESPACE_STD
 
-#endif // _LIBCPP_STD_VER >= 23
+#  endif // _LIBCPP_STD_VER >= 23
 
 _LIBCPP_POP_MACROS
 
+#endif // __cplusplus < 201103L && defined(_LIBCPP_USE_FROZEN_CXX03_HEADERS)
+
 #endif // _LIBCPP_GENERATOR



More information about the libcxx-commits mailing list