https://gcc.gnu.org/g:a4e8d1884e0e9ed43add45d71278e618a729ff88

commit r16-120-ga4e8d1884e0e9ed43add45d71278e618a729ff88
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Thu Dec 12 00:32:08 2024 +0000

    libstdc++: Add lvalue overload for generator::yield_value
    
    This was approved in Wrocław as LWG 3899.
    
    This avoids creating a new coroutine frame to co_yield the elements of
    an lvalue generator.
    
    libstdc++-v3/ChangeLog:
    
            * include/std/generator (generator::yield_value): Add overload
            taking lvalue element_of view, as per LWG 3899.
            * testsuite/24_iterators/range_generators/lwg3899.cc: New test.
    
    Reviewed-by: Tomasz Kamiński <tkami...@redhat.com>
    Reviewed-by: Arsen Arsenović <ar...@aarsen.me>

Diff:
---
 libstdc++-v3/include/std/generator                 | 10 ++++
 .../24_iterators/range_generators/lwg3899.cc       | 57 ++++++++++++++++++++++
 2 files changed, 67 insertions(+)

diff --git a/libstdc++-v3/include/std/generator 
b/libstdc++-v3/include/std/generator
index 3f781f1bb29f..7ab2c9e7ce9d 100644
--- a/libstdc++-v3/include/std/generator
+++ b/libstdc++-v3/include/std/generator
@@ -153,6 +153,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          noexcept
        { return _Recursive_awaiter { std::move(__r.range) }; }
 
+       // _GLIBCXX_RESOLVE_LIB_DEFECTS
+       // 3899. co_yielding elements of an lvalue generator is
+       // unnecessarily inefficient
+       template<typename _R2, typename _V2, typename _A2, typename _U2>
+       requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded>
+       auto
+       yield_value(ranges::elements_of<generator<_R2, _V2, _A2>&, _U2> __r)
+         noexcept
+       { return _Recursive_awaiter { std::move(__r.range) }; }
+
        template<ranges::input_range _R, typename _Alloc>
        requires convertible_to<ranges::range_reference_t<_R>, _Yielded>
        auto
diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc 
b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc
new file mode 100644
index 000000000000..5a812ecf9d97
--- /dev/null
+++ b/libstdc++-v3/testsuite/24_iterators/range_generators/lwg3899.cc
@@ -0,0 +1,57 @@
+// { dg-do run { target c++23 } }
+
+// LWG 3899.
+// co_yielding elements of an lvalue generator is unnecessarily inefficient
+
+#include <generator>
+#include <memory_resource>
+#include <testsuite_hooks.h>
+
+struct memory_resource : std::pmr::memory_resource
+{
+  std::size_t count = 0;
+
+  void* do_allocate(std::size_t n, std::size_t a) override
+  {
+    count += n;
+    return std::pmr::new_delete_resource()->allocate(n, a);
+  }
+
+  void do_deallocate(void* p, std::size_t n, std::size_t a) override
+  {
+    return std::pmr::new_delete_resource()->deallocate(p, n, a);
+  }
+
+  bool do_is_equal(const std::pmr::memory_resource& mr) const noexcept override
+  { return this == &mr; }
+};
+
+std::pmr::generator<int>
+f(std::allocator_arg_t, std::pmr::polymorphic_allocator<>, int init)
+{
+  co_yield init + 0;
+  co_yield init + 1;
+}
+
+std::pmr::generator<int>
+g(std::allocator_arg_t, std::pmr::polymorphic_allocator<> alloc)
+{
+  auto gen = f(std::allocator_arg, alloc, 0);
+  auto gen2 = f(std::allocator_arg, alloc, 2);
+  co_yield std::ranges::elements_of(std::move(gen), alloc);
+  co_yield std::ranges::elements_of(gen2, alloc);
+}
+
+int
+main()
+{
+  std::size_t counts[4];
+  memory_resource mr;
+  for (auto d : g(std::allocator_arg , &mr))
+    counts[d] = mr.count;
+  VERIFY(counts[0] != 0);
+  // No allocations after the first one:
+  VERIFY(counts[1] == counts[0]);
+  VERIFY(counts[2] == counts[0]);
+  VERIFY(counts[3] == counts[0]);
+}

Reply via email to