libstdc++: implement std::generator
Checks
Commit Message
libstdc++-v3/ChangeLog:
* include/Makefile.am: Install std/generator, bits/elements_of.h
as freestanding.
* include/Makefile.in: Regenerate.
* include/bits/version.def: Add __cpp_lib_generator.
* include/bits/version.h: Regenerate.
* include/precompiled/stdc++.h: Include <generator>.
* include/std/ranges: Include bits/elements_of.h
* include/bits/elements_of.h: New file.
* include/std/generator: New file.
* testsuite/24_iterators/range_generators/01.cc: New test.
* testsuite/24_iterators/range_generators/02.cc: New test.
* testsuite/24_iterators/range_generators/copy.cc: New test.
* testsuite/24_iterators/range_generators/except.cc: New test.
* testsuite/24_iterators/range_generators/synopsis.cc: New test.
* testsuite/24_iterators/range_generators/subrange.cc: New test.
---
Evening,
This is an implementation of <generator> from C++23. It should be
feature-complete, though it doesn't have all the tests that it ought to
and is missing a few tweaks.
Posting to get reviews in the meanwhile, in case something obvious was
missed.
Have a lovely night :-)
libstdc++-v3/include/Makefile.am | 2 +
libstdc++-v3/include/Makefile.in | 2 +
libstdc++-v3/include/bits/elements_of.h | 72 ++
libstdc++-v3/include/bits/version.def | 9 +
libstdc++-v3/include/bits/version.h | 11 +
libstdc++-v3/include/precompiled/stdc++.h | 1 +
libstdc++-v3/include/std/generator | 820 ++++++++++++++++++
libstdc++-v3/include/std/ranges | 4 +
.../24_iterators/range_generators/01.cc | 55 ++
.../24_iterators/range_generators/02.cc | 219 +++++
.../24_iterators/range_generators/copy.cc | 97 +++
.../24_iterators/range_generators/except.cc | 97 +++
.../24_iterators/range_generators/subrange.cc | 45 +
.../24_iterators/range_generators/synopsis.cc | 38 +
14 files changed, 1472 insertions(+)
create mode 100644 libstdc++-v3/include/bits/elements_of.h
create mode 100644 libstdc++-v3/include/std/generator
create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/01.cc
create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/02.cc
create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc
create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/except.cc
create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc
create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc
Comments
On Sat, 18 Nov 2023 at 19:50, Arsen Arsenović <arsen@aarsen.me> wrote:
>
> libstdc++-v3/ChangeLog:
>
> * include/Makefile.am: Install std/generator, bits/elements_of.h
> as freestanding.
> * include/Makefile.in: Regenerate.
> * include/bits/version.def: Add __cpp_lib_generator.
> * include/bits/version.h: Regenerate.
> * include/precompiled/stdc++.h: Include <generator>.
> * include/std/ranges: Include bits/elements_of.h
> * include/bits/elements_of.h: New file.
> * include/std/generator: New file.
> * testsuite/24_iterators/range_generators/01.cc: New test.
> * testsuite/24_iterators/range_generators/02.cc: New test.
> * testsuite/24_iterators/range_generators/copy.cc: New test.
> * testsuite/24_iterators/range_generators/except.cc: New test.
> * testsuite/24_iterators/range_generators/synopsis.cc: New test.
> * testsuite/24_iterators/range_generators/subrange.cc: New test.
> ---
> Evening,
>
> This is an implementation of <generator> from C++23. It should be
> feature-complete, though it doesn't have all the tests that it ought to
> and is missing a few tweaks.
>
> Posting to get reviews in the meanwhile, in case something obvious was
> missed.
>
> Have a lovely night :-)
>
> libstdc++-v3/include/Makefile.am | 2 +
> libstdc++-v3/include/Makefile.in | 2 +
> libstdc++-v3/include/bits/elements_of.h | 72 ++
> libstdc++-v3/include/bits/version.def | 9 +
> libstdc++-v3/include/bits/version.h | 11 +
> libstdc++-v3/include/precompiled/stdc++.h | 1 +
> libstdc++-v3/include/std/generator | 820 ++++++++++++++++++
> libstdc++-v3/include/std/ranges | 4 +
> .../24_iterators/range_generators/01.cc | 55 ++
> .../24_iterators/range_generators/02.cc | 219 +++++
> .../24_iterators/range_generators/copy.cc | 97 +++
> .../24_iterators/range_generators/except.cc | 97 +++
> .../24_iterators/range_generators/subrange.cc | 45 +
> .../24_iterators/range_generators/synopsis.cc | 38 +
> 14 files changed, 1472 insertions(+)
> create mode 100644 libstdc++-v3/include/bits/elements_of.h
> create mode 100644 libstdc++-v3/include/std/generator
> create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/01.cc
> create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/02.cc
> create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc
> create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/except.cc
> create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc
> create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc
>
> diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
> index 17d9d9cec313..0b764f2b8a9e 100644
> --- a/libstdc++-v3/include/Makefile.am
> +++ b/libstdc++-v3/include/Makefile.am
> @@ -35,6 +35,7 @@ std_freestanding = \
> ${std_srcdir}/coroutine \
> ${std_srcdir}/expected \
> ${std_srcdir}/functional \
> + ${std_srcdir}/generator \
> ${std_srcdir}/iterator \
> ${std_srcdir}/limits \
> ${std_srcdir}/memory \
> @@ -122,6 +123,7 @@ bits_freestanding = \
> ${bits_srcdir}/concept_check.h \
> ${bits_srcdir}/char_traits.h \
> ${bits_srcdir}/cpp_type_traits.h \
> + ${bits_srcdir}/elements_of.h \
> ${bits_srcdir}/enable_special_members.h \
> ${bits_srcdir}/functexcept.h \
> ${bits_srcdir}/functional_hash.h \
> diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
> index f038af709cc4..7f1a6592942e 100644
> --- a/libstdc++-v3/include/Makefile.in
> +++ b/libstdc++-v3/include/Makefile.in
> @@ -393,6 +393,7 @@ std_freestanding = \
> ${std_srcdir}/coroutine \
> ${std_srcdir}/expected \
> ${std_srcdir}/functional \
> + ${std_srcdir}/generator \
> ${std_srcdir}/iterator \
> ${std_srcdir}/limits \
> ${std_srcdir}/memory \
> @@ -477,6 +478,7 @@ bits_freestanding = \
> ${bits_srcdir}/concept_check.h \
> ${bits_srcdir}/char_traits.h \
> ${bits_srcdir}/cpp_type_traits.h \
> + ${bits_srcdir}/elements_of.h \
> ${bits_srcdir}/enable_special_members.h \
> ${bits_srcdir}/functexcept.h \
> ${bits_srcdir}/functional_hash.h \
> diff --git a/libstdc++-v3/include/bits/elements_of.h b/libstdc++-v3/include/bits/elements_of.h
> new file mode 100644
> index 000000000000..663e15a94aa7
> --- /dev/null
> +++ b/libstdc++-v3/include/bits/elements_of.h
> @@ -0,0 +1,72 @@
> +// Tag type for yielding ranges rather than values in <generator> -*- C++ -*-
> +
> +// Copyright (C) 2023 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +#ifndef _GLIBCXX_BITS_ELEMENTS_OF
> +#define _GLIBCXX_BITS_ELEMENTS_OF
> +
> +#pragma GCC system_header
> +
> +#include <bits/c++config.h>
> +
> +#include <bits/version.h>
> +
> +// C++ >= 23 && __glibcxx_coroutine
> +#if defined(__glibcxx_ranges) && defined(__glibcxx_generator)
> +#include <bits/ranges_base.h>
> +#include <bits/memoryfwd.h>
> +
> +#if _GLIBCXX_HOSTED
> +# include <bits/allocator.h> // likely desirable if hosted.
> +#endif // HOSTED
> +
> +namespace std _GLIBCXX_VISIBILITY(default)
> +{
> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> +namespace ranges
> +{
> +
> + /**
> + * @ingroup ranges
> + * @since C++23
> + * @{
> + */
> +
> + template<range _Range, typename _Alloc = allocator<byte>>
> + struct elements_of
> + {
> + [[no_unique_address]] _Range range;
> + [[no_unique_address]] _Alloc allocator = _Alloc();
> + };
> +
> + template<typename _Range, typename _Alloc = allocator<byte>>
> + elements_of(_Range&&, _Alloc = _Alloc())
> + -> elements_of<_Range&&, _Alloc>;
> +
> + /// @}
> +}
> +_GLIBCXX_END_NAMESPACE_VERSION
> +} // namespace std
> +
> +#endif // __glibcxx_generator && __glibcxx_ranges
> +#endif // _GLIBCXX_BITS_ELEMENTS_OF
> diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
> index 447fdeb95193..1edee0dbd27b 100644
> --- a/libstdc++-v3/include/bits/version.def
> +++ b/libstdc++-v3/include/bits/version.def
> @@ -1649,6 +1649,15 @@ ftms = {
> };
> };
>
> +ftms = {
> + name = generator;
> + values = {
> + v = 202207;
> + cxxmin = 23;
> + extra_cond = "__glibcxx_coroutine";
> + };
> +};
> +
> // Standard test specifications.
> stds[97] = ">= 199711L";
> stds[03] = ">= 199711L";
> diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
> index 97c6d8508f48..b323ea9dab9e 100644
> --- a/libstdc++-v3/include/bits/version.h
> +++ b/libstdc++-v3/include/bits/version.h
> @@ -2021,4 +2021,15 @@
> #endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) */
> #undef __glibcxx_want_to_string
>
> +// from version.def line 1637
> +#if !defined(__cpp_lib_generator)
> +# if (__cplusplus >= 202302L) && (__glibcxx_coroutine)
> +# define __glibcxx_generator 202207L
> +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_generator)
> +# define __cpp_lib_generator 202207L
> +# endif
> +# endif
> +#endif /* !defined(__cpp_lib_generator) && defined(__glibcxx_want_generator) */
> +#undef __glibcxx_want_generator
> +
> #undef __glibcxx_want_all
> diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h
> index 176ad79ff3c3..baad95dd6c3e 100644
> --- a/libstdc++-v3/include/precompiled/stdc++.h
> +++ b/libstdc++-v3/include/precompiled/stdc++.h
> @@ -222,6 +222,7 @@
>
> #if __cplusplus > 202002L
> #include <expected>
> +#include <generator>
> #include <spanstream>
> #if __has_include(<stacktrace>)
> # include <stacktrace>
> diff --git a/libstdc++-v3/include/std/generator b/libstdc++-v3/include/std/generator
> new file mode 100644
> index 000000000000..a06811d3dd96
> --- /dev/null
> +++ b/libstdc++-v3/include/std/generator
> @@ -0,0 +1,820 @@
> +// <generator> -*- C++ -*-
> +
> +// Copyright (C) 2023 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +/** @file include/generator
> + * This is a Standard C++ Library header.
> + */
> +
> +#ifndef _GLIBCXX_GENERATOR
> +#define _GLIBCXX_GENERATOR
> +
> +#include <ranges>
> +#pragma GCC system_header
> +
> +#include <bits/c++config.h>
> +
> +#define __glibcxx_want_generator
> +#include <bits/version.h>
> +
> +#if __cplusplus < 202302L
> +# error "std::generator is a C++23 extension"
> +#endif
Please remove this. Giving an error when a header is included breaks
this pattern:
#if __has_include(<generator>)
#include <generator>
#ifdef __cpp_lib_generator
// ... use it ...
#endif
We have such errors for C++11 headers, but those predate the existence
of __has_include and feature test macros anyway. For new headers from
C++14 and later we don't do an #error. The header should just be
empty.
> +
> +#ifdef __cpp_lib_generator // C++ >= 23 && __glibcxx_coroutine
> +#include <new>
> +#include <bits/move.h>
> +#include <bits/ranges_util.h>
> +#include <bits/elements_of.h>
> +#include <bits/uses_allocator.h>
> +#include <bits/exception_ptr.h>
> +#include <cstddef>
> +#include <cstdint>
> +#include <cstring>
> +#include <coroutine>
> +
> +#include <type_traits>
> +#include <variant>
> +#include <concepts>
> +
> +#if _GLIBCXX_HOSTED
> +# include <bits/memory_resource.h>
> +#endif // HOSTED
> +
> +namespace std _GLIBCXX_VISIBILITY(default)
> +{
> +_GLIBCXX_BEGIN_NAMESPACE_VERSION
> +
> + /**
> + * @defgroup generator_coros Range generator coroutines
> + * @addtogroup ranges
> + * @since C++23
> + * @{
> + */
> +
> + /** @brief A range specified using a yielding coroutine.
> + *
> + * `std::generator` is a utility class for defining ranges using coroutines
> + * that yield elements as a range. Generator coroutines are synchronous.
> + *
> + * @headerfile generator
> + * @since C++23
> + */
> + template<typename _Ref, typename _V = void, typename _Alloc = void>
> + class generator;
> +
> + /// @cond undocumented
> + namespace __gen
> + {
> + /// _Reference type for a generator whose reference (first argument) and
> + /// value (second argument) types are _Ref and _V.
> + template<typename _Ref, typename _V>
We avoid single letter names, because they clash with libc macros like
_T and _X. I don't think _V is actually used by any libc, but for
consistency please avoid it. So maybe _Val instead.
> + using _Reference_t = __conditional_t<is_void_v<_V>,
> + _Ref&&, _Ref>;
> +
> + /// Type yielded by a generator whose _Reference type is _Reference.
> + template<typename _Reference>
> + using _Yield_t = __conditional_t<is_reference_v<_Reference>,
> + _Reference,
> + const _Reference&>;
> +
> + /// _Yield_t * _Reference_t
> + template<typename _Ref, typename _V>
> + using _Yield2_t = _Yield_t<_Reference_t<_Ref, _V>>;
> +
> + template<typename> struct _Is_generator_t : std::false_type {};
> + template<typename _V, typename _R, typename _A>
> + struct _Is_generator_t<::std::generator<_V, _R, _A>> : std::true_type {};
> +
> + template<typename _T>
> + concept _Is_generator = _Is_generator_t<remove_cvref_t<_T>>::value;
We usually name concepts all lowercase, so __is_generator. But it
looks like we don't need a concept here anyway, it's only tested in a
static_assert, which can use any boolean constant. We don't need to
use _Is_generator in requires-clauses or anything like that. Unless
the intention is to allow users to specialize the _Is_generator_t
class template for their own types (is it?) then that class template
and the concept can be replaced with a variable template, which will
compile more efficiently:
template<typename> constexpr bool __is_generator = false;
template<typename _Val, typename _Ref, typename _Alloc>
constexpr bool __is_generator<std::generator<_Val, _Ref, _Alloc>> = true;
Then this can be used directly in the static_assert.
Alternatively, the __is_specialization_of variable template in
<format> could be moved to another header, and then you could just use
__is_specialization_of<std::generator>.
> +
> + /// Allocator and value type erased generator promise type.
> + /// \tparam _Yielded The corresponding generators yielded type.
> + template<typename _Yielded>
> + class _Promise_erased
> + {
> + static_assert(is_reference_v<_Yielded>);
> + using _Yielded_deref = remove_reference_t<_Yielded>;
> + using _Yielded_decvref = remove_cvref_t<_Yielded>;
> + using _ValuePtr = add_pointer_t<_Yielded>;
> + using _Coro_handle = std::coroutine_handle<_Promise_erased>;
> +
> + template<typename, typename, typename>
> + friend struct std::generator;
s/struct/class/
> +
> + template<typename _Gen>
> + struct _Recursive_awaiter;
> + template<typename>
> + friend struct _Recursive_awaiter;
> + struct _Copy_awaiter;
> + struct _Subyield_state;
> + struct _Final_awaiter;
> + public:
> + suspend_always
> + initial_suspend() const noexcept
> + { return {}; }
> +
> + suspend_always
> + yield_value(_Yielded __val) noexcept
> + {
> + _M_bottom_value() = ::std::addressof(__val);
> + return {};
> + }
> +
> + auto
> + yield_value(const _Yielded_deref& __val)
> + noexcept (is_nothrow_constructible_v<_Yielded_decvref,
> + const _Yielded_deref&>)
> + requires (is_rvalue_reference_v<_Yielded>
> + && constructible_from<_Yielded_decvref,
> + const _Yielded_deref&>)
> + { return _Copy_awaiter(__val, _M_bottom_value()); }
> +
> + 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
> + yield_value(ranges::elements_of<_R, _Alloc> __r)
> + noexcept
> + {
> + auto __n = [] (allocator_arg_t, _Alloc,
> + ranges::iterator_t<_R> __i,
> + ranges::sentinel_t<_R> __s)
> + -> generator<_Yielded, ranges::range_value_t<_R>, _Alloc> {
> + for (; __i != __s; ++__i)
> + co_yield static_cast<_Yielded>(*__i);
> + };
> + return yield_value(ranges::elements_of(__n(allocator_arg,
> + __r.allocator,
> + ranges::begin(__r.range),
> + ranges::end(__r.range))));
> + }
> +
> +
> + _Final_awaiter
> + final_suspend() noexcept
> + { return {}; }
> +
> + void
> + unhandled_exception()
> + {
> + // To get to this point, this coroutine must have been active. In that
> + // case, it must be the top of the stack. The current coroutine is
> + // the sole entry of the stack iff it is both the top and the bottom. As
> + // it is the top implicitly in this context it will be the sole entry iff
> + // it is the bottom.
> + if (_M_nest._M_is_bottom())
> + throw;
> + else
> + this->_M_except = std::current_exception();
> + }
> +
> + void await_transform() = delete;
> + void return_void() const noexcept {}
> +
> + private:
> + _ValuePtr&
> + _M_bottom_value() noexcept
> + { return _M_nest._M_bottom_value(*this); }
> +
> + _ValuePtr&
> + _M_value() noexcept
> + { return _M_nest._M_value(*this); }
> +
> + _Subyield_state _M_nest;
> + std::exception_ptr _M_except;
> + };
> +
> + template<typename _Yielded>
> + struct _Promise_erased<_Yielded>::_Subyield_state
> + {
> + struct _Frame
> + {
> + _Coro_handle _M_bottom;
> + _Coro_handle _M_parent;
> + };
> +
> + struct _Bottom_frame
> + {
> + _Coro_handle _M_top;
> + _ValuePtr _M_value = nullptr;
> + };
> +
> + std::variant<
> + _Bottom_frame,
> + _Frame
> + > _M_stack;
> +
> + bool
> + _M_is_bottom() const noexcept
> + { return !std::holds_alternative<_Frame>(this->_M_stack); }
> +
> + _Coro_handle&
> + _M_top() noexcept
> + {
> + if (auto __f = std::get_if<_Frame>(&this->_M_stack))
> + return __f->_M_bottom.promise()._M_nest._M_top();
> +
> + auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack);
> + __glibcxx_assert(__bf);
> + return __bf->_M_top;
> + }
> +
> + void
> + _M_push(_Coro_handle __current, _Coro_handle __subyield) noexcept
> + {
> + __glibcxx_assert(&__current.promise()._M_nest == this);
> + __glibcxx_assert(this->_M_top() == __current);
> +
> + __subyield.promise()._M_nest._M_jump_in(__current, __subyield);
> + }
> +
> + std::coroutine_handle<>
> + _M_pop() noexcept
> + {
> + if (auto __f = std::get_if<_Frame>(&this->_M_stack))
> + {
> + // We aren't a bottom coroutine. Restore the parent to the top
> + // and resume.
> + auto __p = this->_M_top() = __f->_M_parent;
> + return __p;
> + }
> + else
> + // Otherwise, there's nothing to resume.
> + return std::noop_coroutine();
> + }
> +
> + void
> + _M_jump_in(_Coro_handle __rest, _Coro_handle __new) noexcept
> + {
> + __glibcxx_assert(&__new.promise()._M_nest == this);
> + __glibcxx_assert(this->_M_is_bottom());
> + // We're bottom. We're also top of top is unset (note that this is
> + // not true if something was added to the coro stack and then popped,
> + // but in that case we can't possibly be yielded from, as it would
> + // require rerunning begin()).
> + __glibcxx_assert(!this->_M_top());
> +
> + auto& __rn = __rest.promise()._M_nest;
> + __rn._M_top() = __new;
> +
> + // Presume we're the second frame...
> + auto& __bott = __rest;
> + if (auto __f = std::get_if<_Frame>(&__rn._M_stack))
> + // But, if we aren't, get the actual bottom. We're only the second
> + // frame if our parent is the bottom frame, i.e. it doesn't have a
> + // _Frame member.
> + __bott = __f->_M_bottom;
> +
> + this->_M_stack = _Frame {
> + ._M_bottom = __bott,
> + ._M_parent = __rest
> + };
> + }
> +
> + _ValuePtr&
> + _M_bottom_value(_Promise_erased& __current) noexcept
> + {
> + __glibcxx_assert(&__current._M_nest == this);
> + if (auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack))
> + return __bf->_M_value;
> + auto __f = std::get_if<_Frame>(&this->_M_stack);
> + __glibcxx_assert(__f);
> + auto& __p = __f->_M_bottom.promise();
> + return __p._M_nest._M_value(__p);
> + }
> +
> + _ValuePtr&
> + _M_value(_Promise_erased& __current) noexcept
> + {
> + __glibcxx_assert(&__current._M_nest == this);
> + auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack);
> + __glibcxx_assert(__bf);
> + return __bf->_M_value;
> + }
> + };
> +
> + template<typename _Yielded>
> + struct _Promise_erased<_Yielded>::_Final_awaiter
> + {
> + bool await_ready() noexcept
> + { return false; }
> +
> + template<typename _Promise>
> + auto await_suspend(std::coroutine_handle<_Promise> __c) noexcept
> + {
> + static_assert(is_pointer_interconvertible_base_of_v<
> + _Promise_erased, _Promise>);
> +
> + auto& __n = __c.promise()._M_nest;
> + return __n._M_pop();
> + }
> +
> + void await_resume() noexcept {}
> + };
> +
> + template<typename _Yielded>
> + struct _Promise_erased<_Yielded>::_Copy_awaiter
> + {
> + _Yielded_decvref _M_value;
> + _ValuePtr& _M_bottom_value;
> +
> + constexpr bool await_ready() noexcept
> + { return false; }
> +
> + template<typename _Promise>
> + void await_suspend(std::coroutine_handle<_Promise>) noexcept
> + {
> + static_assert(is_pointer_interconvertible_base_of_v<
> + _Promise_erased, _Promise>);
> + _M_bottom_value = ::std::addressof(_M_value);
> + }
> +
> + constexpr void
> + await_resume() const noexcept
> + {}
> + };
> +
> + template<typename _Yielded>
> + template<typename _Gen>
> + struct _Promise_erased<_Yielded>::_Recursive_awaiter
> + {
> + _Gen _M_gen;
> + static_assert(_Is_generator<_Gen>);
> + static_assert(std::same_as<typename _Gen::yielded, _Yielded>);
> +
> + _Recursive_awaiter(_Gen __gen) noexcept
> + : _M_gen(std::move(__gen))
> + { this->_M_gen._M_mark_as_started(); }
> +
> + constexpr bool
> + await_ready() const noexcept
> + { return false; }
> +
> +
> + template<typename _Promise>
> + std::coroutine_handle<>
> + await_suspend(std::coroutine_handle<_Promise> __p) noexcept
> + {
> + static_assert(is_pointer_interconvertible_base_of_v<
> + _Promise_erased, _Promise>);
> +
> + auto __c = _Coro_handle::from_address(__p.address());
> + auto __t = _Coro_handle::from_address(this->_M_gen._M_coro.address());
> + __p.promise()._M_nest._M_push(__c, __t);
> + return __t;
> + }
> +
> + void await_resume()
> + {
> + if (auto __e = _M_gen._M_coro.promise()._M_except)
> + std::rethrow_exception(__e);
> + }
> + };
> +
> + struct _Alloc_block
> + {
> + alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
> + char _M_data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
> +
> + static auto
> + _M_cnt(std::size_t __sz) noexcept
> + {
> + auto __blksz = sizeof(_Alloc_block);
> + return (__sz + __blksz - 1) / __blksz;
> + }
> + };
> +
> + template<typename _A>
> + concept _Stateless_alloc = (allocator_traits<_A>::is_always_equal::value
> + && default_initializable<_A>);
> +
> + template<typename _Alloc>
> + class _Promise_alloc
> + {
> + using _ATr = allocator_traits<_Alloc>;
> + using _Rebound = typename _ATr::template rebind_alloc<_Alloc_block>;
> + using _Rebound_ATr = typename _ATr
> + ::template rebind_traits<_Alloc_block>;
> + static_assert(is_pointer_v<typename _Rebound_ATr::pointer>,
> + "Must use allocators for true pointers with generators");
> +
> + static auto
> + _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
> + {
> + auto __an = __fn + __fsz;
> + auto __ba = alignof(_Rebound);
> + return reinterpret_cast<_Rebound*>(((__an + __ba - 1) / __ba) * __ba);
> + }
> +
> + static auto
> + _M_alloc_size(std::size_t __csz) noexcept
> + {
> + auto __ba = alignof(_Rebound);
> + // Our desired layout is placing the coroutine frame, then pad out to
> + // align, then place the allocator. The total size of that is the
> + // size of the coroutine frame, plus up to __ba bytes, plus the size
> + // of the allocator.
> + return __csz + __ba + sizeof(_Rebound);
> + }
> +
> + static void*
> + _M_allocate(_Rebound __b, std::size_t __csz)
> + {
> + if constexpr (_Stateless_alloc<_Rebound>)
> + // Only need room for the coroutine.
> + return __b.allocate(_Alloc_block::_M_cnt(__csz));
> + else
> + {
> + auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz));
> + auto __f = __b.allocate(__nsz);
> + auto __fn = reinterpret_cast<std::uintptr_t>(__f);
> + auto __an = _M_alloc_address(__fn, __csz);
> + ::new (__an) _Rebound(std::move(__b));
> + return __f;
> + }
> + }
> +
> + public:
> + void*
> + operator new(std::size_t __sz)
> + requires default_initializable<_Rebound> // _Alloc is non-void
> + { return _M_allocate({}, __sz); }
> +
> + template<typename _NA, typename... _Args>
> + void*
> + operator new(std::size_t __sz,
> + allocator_arg_t, const _NA& __na,
> + const _Args&...)
> + requires convertible_to<const _NA&, _Alloc>
> + {
> + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)),
> + __sz);
> + }
> +
> + template<typename _This, typename _NA, typename... _Args>
> + void*
> + operator new(std::size_t __sz,
> + const _This&,
> + allocator_arg_t, const _NA& __na,
> + const _Args&...)
> + requires convertible_to<const _NA&, _Alloc>
> + {
> + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)),
> + __sz);
> + }
> +
> + void
> + operator delete(void* __ptr, std::size_t __csz) noexcept
> + {
> + if constexpr (_Stateless_alloc<_Rebound>)
> + {
> + _Rebound __b;
> + return __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr),
> + _Alloc_block::_M_cnt(__csz));
> + }
> + else
> + {
> + auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz));
> + auto __fn = reinterpret_cast<std::uintptr_t>(__ptr);
> + auto __an = _M_alloc_address(__fn, __csz);
> + _Rebound __b(std::move(*__an));
> + __an->~_Rebound();
> + __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nsz);
> + }
> + }
> + };
> +
> + template<>
> + class _Promise_alloc<void>
> + {
> + using _Dealloc_fn = void (*)(void*, std::size_t);
> +
> + static auto
> + _M_dealloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
> + {
> + auto __an = __fn + __fsz;
> + auto __ba = alignof(_Dealloc_fn);
> + auto __aligned = ((__an + __ba - 1) / __ba) * __ba;
> + return reinterpret_cast<_Dealloc_fn*>(__aligned);
> + }
> +
> + template<typename _Rebound>
> + static auto
> + _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
> + requires (!_Stateless_alloc<_Rebound>)
> + {
> + auto __ba = alignof(_Rebound);
> + auto __da = _M_dealloc_address(__fn, __fsz);
> + auto __aan = reinterpret_cast<std::uintptr_t>(__da);
> + __aan += sizeof(_Dealloc_fn);
> + auto __aligned = ((__aan + __ba - 1) / __ba) * __ba;
> + return reinterpret_cast<_Rebound*>(__aligned);
> + }
> +
> + template<typename _Rebound>
> + static auto
> + _M_alloc_size(std::size_t __csz) noexcept
> + {
> + // This time, we want the coroutine frame, then the deallocator
> + // pointer, then the allocator itself, if any.
> + std::size_t __aa = 0;
> + std::size_t __as = 0;
> + if constexpr (!std::same_as<_Rebound, void>)
> + {
> + __aa = alignof(_Rebound);
> + __as = sizeof(_Rebound);
> + }
> + auto __ba = __aa + alignof(_Dealloc_fn);
> + return __csz + __ba + __as + sizeof(_Dealloc_fn);
> + }
> +
> + template<typename _Rebound>
> + static void
> + _M_deallocator(void* __ptr, std::size_t __csz) noexcept
> + {
> + auto __asz = _M_alloc_size<_Rebound>(__csz);
> + auto __nblk = _Alloc_block::_M_cnt(__asz);
> +
> + if constexpr (_Stateless_alloc<_Rebound>)
> + {
> + _Rebound __b;
> + __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nblk);
> + }
> + else
> + {
> + auto __fn = reinterpret_cast<std::uintptr_t>(__ptr);
> + auto __an = _M_alloc_address<_Rebound>(__fn, __csz);
> + _Rebound __b(std::move(*__an));
> + __an->~_Rebound();
> + __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nblk);
> + }
> + }
> +
> + template<typename _NA>
> + static void*
> + _M_allocate(const _NA& __na, std::size_t __csz)
> + {
> + using _Rebound = typename std::allocator_traits<_NA>
> + ::template rebind_alloc<_Alloc_block>;
> + using _Rebound_ATr = typename std::allocator_traits<_NA>
> + ::template rebind_traits<_Alloc_block>;
> +
> + static_assert(is_pointer_v<typename _Rebound_ATr::pointer>,
> + "Must use allocators for true pointers with generators");
> +
> + _Dealloc_fn __d = &_M_deallocator<_Rebound>;
> + auto __b = static_cast<_Rebound>(__na);
> + auto __asz = _M_alloc_size<_Rebound>(__csz);
> + auto __nblk = _Alloc_block::_M_cnt(__asz);
> + void* __p = __b.allocate(__nblk);
> + auto __pn = reinterpret_cast<std::uintptr_t>(__p);
> + *_M_dealloc_address(__pn, __csz) = __d;
> + if constexpr (!_Stateless_alloc<_Rebound>)
> + {
> + auto __an = _M_alloc_address<_Rebound>(__pn, __csz);
> + ::new (__an) _Rebound(std::move(__b));
> + }
> + return __p;
> + }
> + public:
> + void*
> + operator new(std::size_t __sz)
> + {
> + auto __nsz = _M_alloc_size<void>(__sz);
> + _Dealloc_fn __d = [] (void* __ptr, std::size_t __sz)
> + {
> + ::operator delete(__ptr, _M_alloc_size<void>(__sz));
> + };
> + auto __p = ::operator new(__nsz);
> + auto __pn = reinterpret_cast<uintptr_t>(__p);
> + *_M_dealloc_address(__pn, __sz) = __d;
> + return __p;
> + }
> +
> + template<typename _NA, typename... _Args>
Please use _Na instead of _NA so it's not all-caps.
> + void*
> + operator new(std::size_t __sz,
> + allocator_arg_t, const _NA& __na,
> + const _Args&...)
> + { return _M_allocate(__na, __sz); }
> +
> + template<typename _This, typename _NA, typename... _Args>
> + void*
> + operator new(std::size_t __sz,
> + const _This&,
> + allocator_arg_t, const _NA& __na,
> + const _Args&...)
> + { return _M_allocate(__na, __sz); }
> +
> + void
> + operator delete(void* __ptr, std::size_t __sz) noexcept
> + {
> + _Dealloc_fn __d;
> + auto __pn = reinterpret_cast<uintptr_t>(__ptr);
> + __d = *_M_dealloc_address(__pn, __sz);
> + __d(__ptr, __sz);
> + }
> + };
> +
> + template<typename _T>
s/T/_Tp/
> + concept _Cv_unqualified_object = is_object_v<_T>
> + && same_as<_T, remove_cv_t<_T>>;
> + } // namespace __gen
> + /// @endcond
> +
> + template<typename _Ref, typename _V, typename _Alloc>
> + class generator
> + : public ranges::view_interface<generator<_Ref, _V, _Alloc>>
> + {
> + using _Value = __conditional_t<is_void_v<_V>, remove_cvref_t<_Ref>, _V>;
> + static_assert(__gen::_Cv_unqualified_object<_Value>,
> + "Generator value must be a cv-unqualified object type");
> + using _Reference = __gen::_Reference_t<_Ref, _V>;
> + static_assert(is_reference_v<_Reference>
> + || (__gen::_Cv_unqualified_object<_Reference>
> + && copy_constructible<_Reference>),
> + "Generator reference type must be either a cv-unqualified "
> + "object type that is trivially constructible or a "
> + "reference type");
> +
> + using _RRef = __conditional_t<
> + is_reference_v<_Reference>,
> + remove_reference_t<_Reference>&&,
> + _Reference>;
> +
> + /* Required to model indirectly_readable, and input_iterator. */
> + static_assert(common_reference_with<_Reference&&, _Value&&>);
> + static_assert(common_reference_with<_Reference&&, _RRef&&>);
> + static_assert(common_reference_with<_RRef&&, const _Value&>);
> +
> + using _Yielded = __gen::_Yield_t<_Reference>;
> + using _Erased_promise = __gen::_Promise_erased<_Yielded>;
> +
> + struct _Iterator;
> +
> + friend _Erased_promise;
> + friend struct _Erased_promise::_Subyield_state;
> + public:
> + using yielded = _Yielded;
> +
> + struct promise_type : _Erased_promise, __gen::_Promise_alloc<_Alloc>
> + {
> + generator get_return_object() noexcept
> + { return { coroutine_handle<promise_type>::from_promise(*this) }; }
> + };
> +
> + static_assert(is_pointer_interconvertible_base_of_v<_Erased_promise,
> + promise_type>);
> +
> + generator(const generator&) = delete;
> +
> + generator(generator&& __other) noexcept
> + : _M_coro(std::__exchange(__other._M_coro, nullptr)),
> + _M_began(std::__exchange(__other._M_began, false))
> + {}
> +
> + ~generator()
> + {
> + if (auto& __c = this->_M_coro)
> + __c.destroy();
> + }
> +
> + generator&
> + operator=(generator __other) noexcept
> + {
> + swap(__other._M_coro, this->_M_coro);
> + swap(__other._M_began, this->_M_began);
> + }
> +
> + _Iterator
> + begin()
> + {
> + this->_M_mark_as_started();
> + auto __h = _Coro_handle::from_promise(_M_coro.promise());
> + __h.promise()._M_nest._M_top() = __h;
> + return { __h };
> + }
> +
> + default_sentinel_t
> + end() const noexcept
> + { return default_sentinel; }
> +
> + private:
> + using _Coro_handle = std::coroutine_handle<_Erased_promise>;
> +
> + generator(coroutine_handle<promise_type> __coro) noexcept
> + : _M_coro { move(__coro) }
> + {}
> +
> + void
> + _M_mark_as_started() noexcept
> + {
> + __glibcxx_assert(!this->_M_began);
> + this->_M_began = true;
> + }
> +
> + coroutine_handle<promise_type> _M_coro;
> + bool _M_began = false;
> + };
> +
> + template<class _Ref, class _V, class _Alloc>
> + struct generator<_Ref, _V, _Alloc>::_Iterator
> + {
> + using value_type = _Value;
> + using difference_type = ptrdiff_t;
> +
> + friend bool
> + operator==(const _Iterator& i, default_sentinel_t) noexcept
> + { return i._M_coro.done(); }
__i
> +
> + friend class generator;
> +
> + _Iterator(_Iterator&& __o) noexcept
> + : _M_coro(std::__exchange(__o._M_coro, {}))
> + {}
> +
> + _Iterator&
> + operator=(_Iterator&& __o) noexcept
> + {
> + this->_M_coro = std::__exchange(__o._M_coro, {});
> + return *this;
> + }
> +
> + _Iterator&
> + operator++()
> + {
> + _M_next();
> + return *this;
> + }
> +
> + void
> + operator++(int)
> + { this->operator++(); }
> +
> + yielded
> + operator*()
> + const noexcept(is_nothrow_move_constructible_v<_Reference>)
> + {
> + auto& __p = this->_M_coro.promise();
> + return static_cast<yielded>(*__p._M_value());
> + }
> +
> + private:
> + friend class generator;
> +
> + _Iterator(_Coro_handle __g)
> + : _M_coro { __g }
> + { this->_M_next(); }
> +
> + void _M_next()
> + {
> + auto& __t = this->_M_coro.promise()._M_nest._M_top();
> + __t.resume();
> + }
> +
> + _Coro_handle _M_coro;
> + };
> +
> + /// @}
> +
> +#if _GLIBCXX_HOSTED
> + namespace pmr {
> + template<class R, class V = void>
s/class/typename/
And uglify the names (_Ref and _Val).
> + using generator = std::generator<R, V, polymorphic_allocator<std::byte>>;
> + }
> +#endif // HOSTED
> +
> +_GLIBCXX_END_NAMESPACE_VERSION
> +} // namespace std
> +#endif // __cpp_lib_generator
> +
> +#endif // _GLIBCXX_GENERATOR
> diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges
> index 26d6c013ad04..fde8c6cd706d 100644
> --- a/libstdc++-v3/include/std/ranges
> +++ b/libstdc++-v3/include/std/ranges
> @@ -67,6 +67,10 @@
> #define __glibcxx_want_ranges_zip
> #include <bits/version.h>
>
> +#ifdef __glibcxx_generator // C++ >= 23 && __glibcxx_coroutine
> +# include <bits/elements_of.h>
> +#endif
> +
> /**
> * @defgroup ranges Ranges
> *
> diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc
> new file mode 100644
> index 000000000000..bedbec3d353f
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc
> @@ -0,0 +1,55 @@
> +// { dg-do run { target c++23 } }
> +// Copyright (C) 2023 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +#include <generator>
> +#include <iostream>
> +
> +// basic example
> +std::generator<int>
> +bar()
> +{
> + co_yield 3;
> + co_yield 4;
> +}
> +
> +std::generator<int>
> +foo()
> +{
> + co_yield 1;
> + co_yield 2;
> + co_yield std::ranges::elements_of { bar() };
> + co_yield 5;
> +}
> +
> +int
> +main()
> +{
> + for (auto x : foo())
> + std::cout << x << '\n';
> +}
> +
> +// { dg-output {1(\n|\r\n|\r)} }
> +// { dg-output {2(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> +// { dg-output {4(\n|\r\n|\r)} }
> +// { dg-output {5(\n|\r\n|\r)} }
> diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc
> new file mode 100644
> index 000000000000..570daedca75b
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc
> @@ -0,0 +1,219 @@
> +// { dg-do run { target c++23 } }
> +// Copyright (C) 2023 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +#include <iostream>
> +#include <generator>
> +#include <testsuite_hooks.h>
> +
> +struct foo
> +{
> + int id;
> +
> + foo(int id)
> + : id { id }
> + {}
> +
> + foo(const foo& o)
> + : id { o.id * 100 }
> + {
> + std::cout << "copy-consed " << o.id << "->" << id << '\n';
> + }
> +
> + foo&
> + operator=(const foo& o)
> + {
> + id = o.id * 100;
> + std::cout << "copied " << o.id << "->" << id << '\n';
> + return *this;
> + }
> +
> + foo(foo&& o)
> + : id { o.id }
> + {
> + o.id = -1;
> + std::cout << "moved " << id << '\n';
> + }
> +
> + foo&
> + operator=(foo&& o)
> + {
> + std::swap(o.id, id);
> + std::cout << "swapped " << id << '\n';
> + return *this;
> + }
> +};
> +
> +std::generator<foo>
> +foogen()
> +{
> + co_yield foo{0};
> +
> + {
> + foo f {1};
> + co_yield f;
> + }
> +
> + {
> + const foo f {2};
> + co_yield f;
> + }
> +
> + {
> + foo f {3};
> + co_yield std::move(f);
> + }
> +}
> +
> +std::generator<const foo&>
> +foogen2()
> +{
> + co_yield foo{0};
> +
> + {
> + foo f {1};
> + co_yield f;
> + }
> +
> + {
> + const foo f {2};
> + co_yield f;
> + }
> +
> + {
> + foo f {3};
> + co_yield std::move(f);
> + }
> +}
> +
> +std::generator<foo&&>
> +foogen3()
> +{
> + co_yield foo{0};
> +
> + {
> + foo f {1};
> + co_yield f;
> + }
> +
> + {
> + const foo f {2};
> + co_yield f;
> + }
> +
> + {
> + foo f {3};
> + co_yield std::move(f);
> + }
> +}
> +
> +int
> +main()
> +{
> + for (auto f : foogen())
> + std::cout << f.id << '\n';
> + for (const auto& f : foogen())
> + std::cout << f.id << '\n';
> + for (auto&& f : foogen())
> + std::cout << f.id << '\n';
> +
> + std::cout << "---\n";
> +
> + for (auto f : foogen2())
> + std::cout << f.id << '\n';
> + for (const auto& f : foogen2())
> + std::cout << f.id << '\n';
> + for (auto&& f : foogen2())
> + std::cout << f.id << '\n';
> +
> + std::cout << "---\n";
> +
> + for (auto f : foogen3())
> + std::cout << f.id << '\n';
> + for (const auto& f : foogen3())
> + std::cout << f.id << '\n';
> + for (auto&& f : foogen3())
> + std::cout << f.id << '\n';
> +}
> +
> +// { dg-output {moved 0(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
> +// { dg-output {moved 100(\n|\r\n|\r)} }
> +// { dg-output {100(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
> +// { dg-output {moved 200(\n|\r\n|\r)} }
> +// { dg-output {200(\n|\r\n|\r)} }
> +// { dg-output {moved 3(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
> +// { dg-output {100(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
> +// { dg-output {200(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
> +// { dg-output {100(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
> +// { dg-output {200(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> +// { dg-output {---(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 0->0(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
> +// { dg-output {100(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
> +// { dg-output {200(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 3->300(\n|\r\n|\r)} }
> +// { dg-output {300(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {1(\n|\r\n|\r)} }
> +// { dg-output {2(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {1(\n|\r\n|\r)} }
> +// { dg-output {2(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> +// { dg-output {---(\n|\r\n|\r)} }
> +// { dg-output {moved 0(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
> +// { dg-output {moved 100(\n|\r\n|\r)} }
> +// { dg-output {100(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
> +// { dg-output {moved 200(\n|\r\n|\r)} }
> +// { dg-output {200(\n|\r\n|\r)} }
> +// { dg-output {moved 3(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
> +// { dg-output {100(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
> +// { dg-output {200(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> +// { dg-output {0(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
> +// { dg-output {100(\n|\r\n|\r)} }
> +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
> +// { dg-output {200(\n|\r\n|\r)} }
> +// { dg-output {3(\n|\r\n|\r)} }
> diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc
> new file mode 100644
> index 000000000000..5e5474d0de53
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc
> @@ -0,0 +1,97 @@
> +// { dg-do run { target c++23 } }
> +// Copyright (C) 2023 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +#include <testsuite_hooks.h>
> +#include <generator>
> +
> +template<unsigned MaxCopies>
> +struct copy_max
> +{
> + int copy = 0;
> +
> + copy_max()
> + {}
> +
> + copy_max(const copy_max& o)
> + : copy {o.copy + 1}
> + {
> + VERIFY(copy <= MaxCopies);
> + }
> +
> + copy_max&
> + operator=(const copy_max& o)
> + {
> + copy = o.copy + 1;
> + VERIFY(copy <= MaxCopies);
> + return *this;
> + }
> +
> + copy_max(copy_max&& o)
> + {
> + std::swap(o.copy, this->copy);
> + }
> +
> + copy_max&
> + operator=(copy_max&& o)
> + {
> + std::swap(o.copy, this->copy);
> + return *this;
> + }
> +};
> +
> +template<typename Ref>
> +std::generator<Ref>
> +foo()
> +{
> + co_yield {};
> +}
> +
> +int
> +main()
> +{
> + static_assert(!std::copy_constructible<std::generator<int>>);
> + {
> + auto gen = foo<const copy_max<1>&>();
> + auto i = gen.begin();
> + *i;
> + *i;
> + auto is = *i;
> + VERIFY(is.copy > 0);
> + }
> +
> + {
> + auto gen2 = foo<copy_max<0>&&>();
> + auto i = gen2.begin();
> + *i;
> + *i;
> + auto is = *i;
> + }
> +
> + {
> + auto gen = foo<copy_max<0>>(); // should be same as case 2
> + auto i = gen.begin();
> + *i;
> + *i;
> + auto is = *i;
> + }
> +}
> diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/except.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/except.cc
> new file mode 100644
> index 000000000000..76f59b0aaed7
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/except.cc
> @@ -0,0 +1,97 @@
> +// { dg-do run { target c++23 } }
> +// Copyright (C) 2023 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +#include <testsuite_hooks.h>
> +#include <generator>
> +
> +std::generator<int>
> +foo()
> +{
> + co_yield 0;
> + throw 3; /* dice roll */
> +}
> +
> +std::generator<int>
> +foo_delegator()
> +{
> + co_yield 1;
> + co_yield std::ranges::elements_of { foo() };
> +}
> +
> +bool catchy_caught = false;
> +
> +std::generator<int>
> +foo_catchy_delegator()
> +{
> + try
> + {
> + co_yield std::ranges::elements_of { foo() };
> + VERIFY(false);
> + }
> + catch (int i)
> + {
> + catchy_caught = true;
> + VERIFY(i == 3);
> + }
> +}
> +
> +int
> +main()
> +{
> + {
> + auto gen = foo();
> + try
> + {
> + auto it = gen.begin();
> + VERIFY(*it == 0);
> + it++;
> + VERIFY(false);
> + }
> + catch (int x)
> + {
> + VERIFY(x == 3);
> + }
> + }
> +
> + {
> + auto gen = foo_delegator();
> + auto it = gen.begin();
> + VERIFY(*it == 1);
> + it++;
> +
> + try
> + {
> + VERIFY(*it == 0);
> + it++;
> + VERIFY(false);
> + }
> + catch (int x)
> + {
> + VERIFY(x == 3);
> + }
> + }
> +
> + for (auto x : foo_catchy_delegator())
> + VERIFY(x == 0);
> + VERIFY(catchy_caught);
> +}
> diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc
> new file mode 100644
> index 000000000000..4bd0b2f9e078
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc
> @@ -0,0 +1,45 @@
> +// { dg-do run { target c++23 } }
> +// Copyright (C) 2023 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +#include <generator>
> +#include <ranges>
> +#include <vector>
> +#include <iostream>
> +
> +std::generator<int&>
> +yield_vector()
> +{
> + std::vector foo { 1, 2, 3 };
> + auto x = 123;
> + co_yield x;
> + co_yield std::ranges::elements_of { foo };
> + x = 456;
> + co_yield x;
> +}
> +
> +int
> +main()
> +{
> + for (auto x : yield_vector())
> + std::cout << x << '\n';
> +}
> diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc
> new file mode 100644
> index 000000000000..6c037f2f3c05
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc
> @@ -0,0 +1,38 @@
> +// { dg-do compile { target c++23 } }
> +// { dg-add-options no_pch }
> +// Copyright (C) 2023 Free Software Foundation, Inc.
> +//
> +// This file is part of the GNU ISO C++ Library. This library is free
> +// software; you can redistribute it and/or modify it under the
> +// terms of the GNU General Public License as published by the
> +// Free Software Foundation; either version 3, or (at your option)
> +// any later version.
> +
> +// This library is distributed in the hope that it will be useful,
> +// but WITHOUT ANY WARRANTY; without even the implied warranty of
> +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +// GNU General Public License for more details.
> +
> +// Under Section 7 of GPL version 3, you are granted additional
> +// permissions described in the GCC Runtime Library Exception, version
> +// 3.1, as published by the Free Software Foundation.
> +
> +// You should have received a copy of the GNU General Public License and
> +// a copy of the GCC Runtime Library Exception along with this program;
> +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
> +// <http://www.gnu.org/licenses/>.
> +
> +#include <generator>
> +
> +#if !defined(__cpp_lib_generator) || __cpp_lib_generator < 202207L
> +# error "__cpp_lib_generator undefined or has wrong value"
> +#endif
> +
> +namespace test {
> + using std::generator;
> +#if __STDC_HOSTED__
> + namespace pmr {
> + using std::pmr::generator;
> + }
> +#endif
> +}
> --
> 2.42.1
>
@@ -35,6 +35,7 @@ std_freestanding = \
${std_srcdir}/coroutine \
${std_srcdir}/expected \
${std_srcdir}/functional \
+ ${std_srcdir}/generator \
${std_srcdir}/iterator \
${std_srcdir}/limits \
${std_srcdir}/memory \
@@ -122,6 +123,7 @@ bits_freestanding = \
${bits_srcdir}/concept_check.h \
${bits_srcdir}/char_traits.h \
${bits_srcdir}/cpp_type_traits.h \
+ ${bits_srcdir}/elements_of.h \
${bits_srcdir}/enable_special_members.h \
${bits_srcdir}/functexcept.h \
${bits_srcdir}/functional_hash.h \
@@ -393,6 +393,7 @@ std_freestanding = \
${std_srcdir}/coroutine \
${std_srcdir}/expected \
${std_srcdir}/functional \
+ ${std_srcdir}/generator \
${std_srcdir}/iterator \
${std_srcdir}/limits \
${std_srcdir}/memory \
@@ -477,6 +478,7 @@ bits_freestanding = \
${bits_srcdir}/concept_check.h \
${bits_srcdir}/char_traits.h \
${bits_srcdir}/cpp_type_traits.h \
+ ${bits_srcdir}/elements_of.h \
${bits_srcdir}/enable_special_members.h \
${bits_srcdir}/functexcept.h \
${bits_srcdir}/functional_hash.h \
new file mode 100644
@@ -0,0 +1,72 @@
+// Tag type for yielding ranges rather than values in <generator> -*- C++ -*-
+
+// Copyright (C) 2023 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#ifndef _GLIBCXX_BITS_ELEMENTS_OF
+#define _GLIBCXX_BITS_ELEMENTS_OF
+
+#pragma GCC system_header
+
+#include <bits/c++config.h>
+
+#include <bits/version.h>
+
+// C++ >= 23 && __glibcxx_coroutine
+#if defined(__glibcxx_ranges) && defined(__glibcxx_generator)
+#include <bits/ranges_base.h>
+#include <bits/memoryfwd.h>
+
+#if _GLIBCXX_HOSTED
+# include <bits/allocator.h> // likely desirable if hosted.
+#endif // HOSTED
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+namespace ranges
+{
+
+ /**
+ * @ingroup ranges
+ * @since C++23
+ * @{
+ */
+
+ template<range _Range, typename _Alloc = allocator<byte>>
+ struct elements_of
+ {
+ [[no_unique_address]] _Range range;
+ [[no_unique_address]] _Alloc allocator = _Alloc();
+ };
+
+ template<typename _Range, typename _Alloc = allocator<byte>>
+ elements_of(_Range&&, _Alloc = _Alloc())
+ -> elements_of<_Range&&, _Alloc>;
+
+ /// @}
+}
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+
+#endif // __glibcxx_generator && __glibcxx_ranges
+#endif // _GLIBCXX_BITS_ELEMENTS_OF
@@ -1649,6 +1649,15 @@ ftms = {
};
};
+ftms = {
+ name = generator;
+ values = {
+ v = 202207;
+ cxxmin = 23;
+ extra_cond = "__glibcxx_coroutine";
+ };
+};
+
// Standard test specifications.
stds[97] = ">= 199711L";
stds[03] = ">= 199711L";
@@ -2021,4 +2021,15 @@
#endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) */
#undef __glibcxx_want_to_string
+// from version.def line 1637
+#if !defined(__cpp_lib_generator)
+# if (__cplusplus >= 202302L) && (__glibcxx_coroutine)
+# define __glibcxx_generator 202207L
+# if defined(__glibcxx_want_all) || defined(__glibcxx_want_generator)
+# define __cpp_lib_generator 202207L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_generator) && defined(__glibcxx_want_generator) */
+#undef __glibcxx_want_generator
+
#undef __glibcxx_want_all
@@ -222,6 +222,7 @@
#if __cplusplus > 202002L
#include <expected>
+#include <generator>
#include <spanstream>
#if __has_include(<stacktrace>)
# include <stacktrace>
new file mode 100644
@@ -0,0 +1,820 @@
+// <generator> -*- C++ -*-
+
+// Copyright (C) 2023 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file include/generator
+ * This is a Standard C++ Library header.
+ */
+
+#ifndef _GLIBCXX_GENERATOR
+#define _GLIBCXX_GENERATOR
+
+#include <ranges>
+#pragma GCC system_header
+
+#include <bits/c++config.h>
+
+#define __glibcxx_want_generator
+#include <bits/version.h>
+
+#if __cplusplus < 202302L
+# error "std::generator is a C++23 extension"
+#endif
+
+#ifdef __cpp_lib_generator // C++ >= 23 && __glibcxx_coroutine
+#include <new>
+#include <bits/move.h>
+#include <bits/ranges_util.h>
+#include <bits/elements_of.h>
+#include <bits/uses_allocator.h>
+#include <bits/exception_ptr.h>
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+#include <coroutine>
+
+#include <type_traits>
+#include <variant>
+#include <concepts>
+
+#if _GLIBCXX_HOSTED
+# include <bits/memory_resource.h>
+#endif // HOSTED
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+ /**
+ * @defgroup generator_coros Range generator coroutines
+ * @addtogroup ranges
+ * @since C++23
+ * @{
+ */
+
+ /** @brief A range specified using a yielding coroutine.
+ *
+ * `std::generator` is a utility class for defining ranges using coroutines
+ * that yield elements as a range. Generator coroutines are synchronous.
+ *
+ * @headerfile generator
+ * @since C++23
+ */
+ template<typename _Ref, typename _V = void, typename _Alloc = void>
+ class generator;
+
+ /// @cond undocumented
+ namespace __gen
+ {
+ /// _Reference type for a generator whose reference (first argument) and
+ /// value (second argument) types are _Ref and _V.
+ template<typename _Ref, typename _V>
+ using _Reference_t = __conditional_t<is_void_v<_V>,
+ _Ref&&, _Ref>;
+
+ /// Type yielded by a generator whose _Reference type is _Reference.
+ template<typename _Reference>
+ using _Yield_t = __conditional_t<is_reference_v<_Reference>,
+ _Reference,
+ const _Reference&>;
+
+ /// _Yield_t * _Reference_t
+ template<typename _Ref, typename _V>
+ using _Yield2_t = _Yield_t<_Reference_t<_Ref, _V>>;
+
+ template<typename> struct _Is_generator_t : std::false_type {};
+ template<typename _V, typename _R, typename _A>
+ struct _Is_generator_t<::std::generator<_V, _R, _A>> : std::true_type {};
+
+ template<typename _T>
+ concept _Is_generator = _Is_generator_t<remove_cvref_t<_T>>::value;
+
+
+ /// Allocator and value type erased generator promise type.
+ /// \tparam _Yielded The corresponding generators yielded type.
+ template<typename _Yielded>
+ class _Promise_erased
+ {
+ static_assert(is_reference_v<_Yielded>);
+ using _Yielded_deref = remove_reference_t<_Yielded>;
+ using _Yielded_decvref = remove_cvref_t<_Yielded>;
+ using _ValuePtr = add_pointer_t<_Yielded>;
+ using _Coro_handle = std::coroutine_handle<_Promise_erased>;
+
+ template<typename, typename, typename>
+ friend struct std::generator;
+
+ template<typename _Gen>
+ struct _Recursive_awaiter;
+ template<typename>
+ friend struct _Recursive_awaiter;
+ struct _Copy_awaiter;
+ struct _Subyield_state;
+ struct _Final_awaiter;
+ public:
+ suspend_always
+ initial_suspend() const noexcept
+ { return {}; }
+
+ suspend_always
+ yield_value(_Yielded __val) noexcept
+ {
+ _M_bottom_value() = ::std::addressof(__val);
+ return {};
+ }
+
+ auto
+ yield_value(const _Yielded_deref& __val)
+ noexcept (is_nothrow_constructible_v<_Yielded_decvref,
+ const _Yielded_deref&>)
+ requires (is_rvalue_reference_v<_Yielded>
+ && constructible_from<_Yielded_decvref,
+ const _Yielded_deref&>)
+ { return _Copy_awaiter(__val, _M_bottom_value()); }
+
+ 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
+ yield_value(ranges::elements_of<_R, _Alloc> __r)
+ noexcept
+ {
+ auto __n = [] (allocator_arg_t, _Alloc,
+ ranges::iterator_t<_R> __i,
+ ranges::sentinel_t<_R> __s)
+ -> generator<_Yielded, ranges::range_value_t<_R>, _Alloc> {
+ for (; __i != __s; ++__i)
+ co_yield static_cast<_Yielded>(*__i);
+ };
+ return yield_value(ranges::elements_of(__n(allocator_arg,
+ __r.allocator,
+ ranges::begin(__r.range),
+ ranges::end(__r.range))));
+ }
+
+
+ _Final_awaiter
+ final_suspend() noexcept
+ { return {}; }
+
+ void
+ unhandled_exception()
+ {
+ // To get to this point, this coroutine must have been active. In that
+ // case, it must be the top of the stack. The current coroutine is
+ // the sole entry of the stack iff it is both the top and the bottom. As
+ // it is the top implicitly in this context it will be the sole entry iff
+ // it is the bottom.
+ if (_M_nest._M_is_bottom())
+ throw;
+ else
+ this->_M_except = std::current_exception();
+ }
+
+ void await_transform() = delete;
+ void return_void() const noexcept {}
+
+ private:
+ _ValuePtr&
+ _M_bottom_value() noexcept
+ { return _M_nest._M_bottom_value(*this); }
+
+ _ValuePtr&
+ _M_value() noexcept
+ { return _M_nest._M_value(*this); }
+
+ _Subyield_state _M_nest;
+ std::exception_ptr _M_except;
+ };
+
+ template<typename _Yielded>
+ struct _Promise_erased<_Yielded>::_Subyield_state
+ {
+ struct _Frame
+ {
+ _Coro_handle _M_bottom;
+ _Coro_handle _M_parent;
+ };
+
+ struct _Bottom_frame
+ {
+ _Coro_handle _M_top;
+ _ValuePtr _M_value = nullptr;
+ };
+
+ std::variant<
+ _Bottom_frame,
+ _Frame
+ > _M_stack;
+
+ bool
+ _M_is_bottom() const noexcept
+ { return !std::holds_alternative<_Frame>(this->_M_stack); }
+
+ _Coro_handle&
+ _M_top() noexcept
+ {
+ if (auto __f = std::get_if<_Frame>(&this->_M_stack))
+ return __f->_M_bottom.promise()._M_nest._M_top();
+
+ auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack);
+ __glibcxx_assert(__bf);
+ return __bf->_M_top;
+ }
+
+ void
+ _M_push(_Coro_handle __current, _Coro_handle __subyield) noexcept
+ {
+ __glibcxx_assert(&__current.promise()._M_nest == this);
+ __glibcxx_assert(this->_M_top() == __current);
+
+ __subyield.promise()._M_nest._M_jump_in(__current, __subyield);
+ }
+
+ std::coroutine_handle<>
+ _M_pop() noexcept
+ {
+ if (auto __f = std::get_if<_Frame>(&this->_M_stack))
+ {
+ // We aren't a bottom coroutine. Restore the parent to the top
+ // and resume.
+ auto __p = this->_M_top() = __f->_M_parent;
+ return __p;
+ }
+ else
+ // Otherwise, there's nothing to resume.
+ return std::noop_coroutine();
+ }
+
+ void
+ _M_jump_in(_Coro_handle __rest, _Coro_handle __new) noexcept
+ {
+ __glibcxx_assert(&__new.promise()._M_nest == this);
+ __glibcxx_assert(this->_M_is_bottom());
+ // We're bottom. We're also top of top is unset (note that this is
+ // not true if something was added to the coro stack and then popped,
+ // but in that case we can't possibly be yielded from, as it would
+ // require rerunning begin()).
+ __glibcxx_assert(!this->_M_top());
+
+ auto& __rn = __rest.promise()._M_nest;
+ __rn._M_top() = __new;
+
+ // Presume we're the second frame...
+ auto& __bott = __rest;
+ if (auto __f = std::get_if<_Frame>(&__rn._M_stack))
+ // But, if we aren't, get the actual bottom. We're only the second
+ // frame if our parent is the bottom frame, i.e. it doesn't have a
+ // _Frame member.
+ __bott = __f->_M_bottom;
+
+ this->_M_stack = _Frame {
+ ._M_bottom = __bott,
+ ._M_parent = __rest
+ };
+ }
+
+ _ValuePtr&
+ _M_bottom_value(_Promise_erased& __current) noexcept
+ {
+ __glibcxx_assert(&__current._M_nest == this);
+ if (auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack))
+ return __bf->_M_value;
+ auto __f = std::get_if<_Frame>(&this->_M_stack);
+ __glibcxx_assert(__f);
+ auto& __p = __f->_M_bottom.promise();
+ return __p._M_nest._M_value(__p);
+ }
+
+ _ValuePtr&
+ _M_value(_Promise_erased& __current) noexcept
+ {
+ __glibcxx_assert(&__current._M_nest == this);
+ auto __bf = std::get_if<_Bottom_frame>(&this->_M_stack);
+ __glibcxx_assert(__bf);
+ return __bf->_M_value;
+ }
+ };
+
+ template<typename _Yielded>
+ struct _Promise_erased<_Yielded>::_Final_awaiter
+ {
+ bool await_ready() noexcept
+ { return false; }
+
+ template<typename _Promise>
+ auto await_suspend(std::coroutine_handle<_Promise> __c) noexcept
+ {
+ static_assert(is_pointer_interconvertible_base_of_v<
+ _Promise_erased, _Promise>);
+
+ auto& __n = __c.promise()._M_nest;
+ return __n._M_pop();
+ }
+
+ void await_resume() noexcept {}
+ };
+
+ template<typename _Yielded>
+ struct _Promise_erased<_Yielded>::_Copy_awaiter
+ {
+ _Yielded_decvref _M_value;
+ _ValuePtr& _M_bottom_value;
+
+ constexpr bool await_ready() noexcept
+ { return false; }
+
+ template<typename _Promise>
+ void await_suspend(std::coroutine_handle<_Promise>) noexcept
+ {
+ static_assert(is_pointer_interconvertible_base_of_v<
+ _Promise_erased, _Promise>);
+ _M_bottom_value = ::std::addressof(_M_value);
+ }
+
+ constexpr void
+ await_resume() const noexcept
+ {}
+ };
+
+ template<typename _Yielded>
+ template<typename _Gen>
+ struct _Promise_erased<_Yielded>::_Recursive_awaiter
+ {
+ _Gen _M_gen;
+ static_assert(_Is_generator<_Gen>);
+ static_assert(std::same_as<typename _Gen::yielded, _Yielded>);
+
+ _Recursive_awaiter(_Gen __gen) noexcept
+ : _M_gen(std::move(__gen))
+ { this->_M_gen._M_mark_as_started(); }
+
+ constexpr bool
+ await_ready() const noexcept
+ { return false; }
+
+
+ template<typename _Promise>
+ std::coroutine_handle<>
+ await_suspend(std::coroutine_handle<_Promise> __p) noexcept
+ {
+ static_assert(is_pointer_interconvertible_base_of_v<
+ _Promise_erased, _Promise>);
+
+ auto __c = _Coro_handle::from_address(__p.address());
+ auto __t = _Coro_handle::from_address(this->_M_gen._M_coro.address());
+ __p.promise()._M_nest._M_push(__c, __t);
+ return __t;
+ }
+
+ void await_resume()
+ {
+ if (auto __e = _M_gen._M_coro.promise()._M_except)
+ std::rethrow_exception(__e);
+ }
+ };
+
+ struct _Alloc_block
+ {
+ alignas(__STDCPP_DEFAULT_NEW_ALIGNMENT__)
+ char _M_data[__STDCPP_DEFAULT_NEW_ALIGNMENT__];
+
+ static auto
+ _M_cnt(std::size_t __sz) noexcept
+ {
+ auto __blksz = sizeof(_Alloc_block);
+ return (__sz + __blksz - 1) / __blksz;
+ }
+ };
+
+ template<typename _A>
+ concept _Stateless_alloc = (allocator_traits<_A>::is_always_equal::value
+ && default_initializable<_A>);
+
+ template<typename _Alloc>
+ class _Promise_alloc
+ {
+ using _ATr = allocator_traits<_Alloc>;
+ using _Rebound = typename _ATr::template rebind_alloc<_Alloc_block>;
+ using _Rebound_ATr = typename _ATr
+ ::template rebind_traits<_Alloc_block>;
+ static_assert(is_pointer_v<typename _Rebound_ATr::pointer>,
+ "Must use allocators for true pointers with generators");
+
+ static auto
+ _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
+ {
+ auto __an = __fn + __fsz;
+ auto __ba = alignof(_Rebound);
+ return reinterpret_cast<_Rebound*>(((__an + __ba - 1) / __ba) * __ba);
+ }
+
+ static auto
+ _M_alloc_size(std::size_t __csz) noexcept
+ {
+ auto __ba = alignof(_Rebound);
+ // Our desired layout is placing the coroutine frame, then pad out to
+ // align, then place the allocator. The total size of that is the
+ // size of the coroutine frame, plus up to __ba bytes, plus the size
+ // of the allocator.
+ return __csz + __ba + sizeof(_Rebound);
+ }
+
+ static void*
+ _M_allocate(_Rebound __b, std::size_t __csz)
+ {
+ if constexpr (_Stateless_alloc<_Rebound>)
+ // Only need room for the coroutine.
+ return __b.allocate(_Alloc_block::_M_cnt(__csz));
+ else
+ {
+ auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz));
+ auto __f = __b.allocate(__nsz);
+ auto __fn = reinterpret_cast<std::uintptr_t>(__f);
+ auto __an = _M_alloc_address(__fn, __csz);
+ ::new (__an) _Rebound(std::move(__b));
+ return __f;
+ }
+ }
+
+ public:
+ void*
+ operator new(std::size_t __sz)
+ requires default_initializable<_Rebound> // _Alloc is non-void
+ { return _M_allocate({}, __sz); }
+
+ template<typename _NA, typename... _Args>
+ void*
+ operator new(std::size_t __sz,
+ allocator_arg_t, const _NA& __na,
+ const _Args&...)
+ requires convertible_to<const _NA&, _Alloc>
+ {
+ return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)),
+ __sz);
+ }
+
+ template<typename _This, typename _NA, typename... _Args>
+ void*
+ operator new(std::size_t __sz,
+ const _This&,
+ allocator_arg_t, const _NA& __na,
+ const _Args&...)
+ requires convertible_to<const _NA&, _Alloc>
+ {
+ return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)),
+ __sz);
+ }
+
+ void
+ operator delete(void* __ptr, std::size_t __csz) noexcept
+ {
+ if constexpr (_Stateless_alloc<_Rebound>)
+ {
+ _Rebound __b;
+ return __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr),
+ _Alloc_block::_M_cnt(__csz));
+ }
+ else
+ {
+ auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz));
+ auto __fn = reinterpret_cast<std::uintptr_t>(__ptr);
+ auto __an = _M_alloc_address(__fn, __csz);
+ _Rebound __b(std::move(*__an));
+ __an->~_Rebound();
+ __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nsz);
+ }
+ }
+ };
+
+ template<>
+ class _Promise_alloc<void>
+ {
+ using _Dealloc_fn = void (*)(void*, std::size_t);
+
+ static auto
+ _M_dealloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
+ {
+ auto __an = __fn + __fsz;
+ auto __ba = alignof(_Dealloc_fn);
+ auto __aligned = ((__an + __ba - 1) / __ba) * __ba;
+ return reinterpret_cast<_Dealloc_fn*>(__aligned);
+ }
+
+ template<typename _Rebound>
+ static auto
+ _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexcept
+ requires (!_Stateless_alloc<_Rebound>)
+ {
+ auto __ba = alignof(_Rebound);
+ auto __da = _M_dealloc_address(__fn, __fsz);
+ auto __aan = reinterpret_cast<std::uintptr_t>(__da);
+ __aan += sizeof(_Dealloc_fn);
+ auto __aligned = ((__aan + __ba - 1) / __ba) * __ba;
+ return reinterpret_cast<_Rebound*>(__aligned);
+ }
+
+ template<typename _Rebound>
+ static auto
+ _M_alloc_size(std::size_t __csz) noexcept
+ {
+ // This time, we want the coroutine frame, then the deallocator
+ // pointer, then the allocator itself, if any.
+ std::size_t __aa = 0;
+ std::size_t __as = 0;
+ if constexpr (!std::same_as<_Rebound, void>)
+ {
+ __aa = alignof(_Rebound);
+ __as = sizeof(_Rebound);
+ }
+ auto __ba = __aa + alignof(_Dealloc_fn);
+ return __csz + __ba + __as + sizeof(_Dealloc_fn);
+ }
+
+ template<typename _Rebound>
+ static void
+ _M_deallocator(void* __ptr, std::size_t __csz) noexcept
+ {
+ auto __asz = _M_alloc_size<_Rebound>(__csz);
+ auto __nblk = _Alloc_block::_M_cnt(__asz);
+
+ if constexpr (_Stateless_alloc<_Rebound>)
+ {
+ _Rebound __b;
+ __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nblk);
+ }
+ else
+ {
+ auto __fn = reinterpret_cast<std::uintptr_t>(__ptr);
+ auto __an = _M_alloc_address<_Rebound>(__fn, __csz);
+ _Rebound __b(std::move(*__an));
+ __an->~_Rebound();
+ __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nblk);
+ }
+ }
+
+ template<typename _NA>
+ static void*
+ _M_allocate(const _NA& __na, std::size_t __csz)
+ {
+ using _Rebound = typename std::allocator_traits<_NA>
+ ::template rebind_alloc<_Alloc_block>;
+ using _Rebound_ATr = typename std::allocator_traits<_NA>
+ ::template rebind_traits<_Alloc_block>;
+
+ static_assert(is_pointer_v<typename _Rebound_ATr::pointer>,
+ "Must use allocators for true pointers with generators");
+
+ _Dealloc_fn __d = &_M_deallocator<_Rebound>;
+ auto __b = static_cast<_Rebound>(__na);
+ auto __asz = _M_alloc_size<_Rebound>(__csz);
+ auto __nblk = _Alloc_block::_M_cnt(__asz);
+ void* __p = __b.allocate(__nblk);
+ auto __pn = reinterpret_cast<std::uintptr_t>(__p);
+ *_M_dealloc_address(__pn, __csz) = __d;
+ if constexpr (!_Stateless_alloc<_Rebound>)
+ {
+ auto __an = _M_alloc_address<_Rebound>(__pn, __csz);
+ ::new (__an) _Rebound(std::move(__b));
+ }
+ return __p;
+ }
+ public:
+ void*
+ operator new(std::size_t __sz)
+ {
+ auto __nsz = _M_alloc_size<void>(__sz);
+ _Dealloc_fn __d = [] (void* __ptr, std::size_t __sz)
+ {
+ ::operator delete(__ptr, _M_alloc_size<void>(__sz));
+ };
+ auto __p = ::operator new(__nsz);
+ auto __pn = reinterpret_cast<uintptr_t>(__p);
+ *_M_dealloc_address(__pn, __sz) = __d;
+ return __p;
+ }
+
+ template<typename _NA, typename... _Args>
+ void*
+ operator new(std::size_t __sz,
+ allocator_arg_t, const _NA& __na,
+ const _Args&...)
+ { return _M_allocate(__na, __sz); }
+
+ template<typename _This, typename _NA, typename... _Args>
+ void*
+ operator new(std::size_t __sz,
+ const _This&,
+ allocator_arg_t, const _NA& __na,
+ const _Args&...)
+ { return _M_allocate(__na, __sz); }
+
+ void
+ operator delete(void* __ptr, std::size_t __sz) noexcept
+ {
+ _Dealloc_fn __d;
+ auto __pn = reinterpret_cast<uintptr_t>(__ptr);
+ __d = *_M_dealloc_address(__pn, __sz);
+ __d(__ptr, __sz);
+ }
+ };
+
+ template<typename _T>
+ concept _Cv_unqualified_object = is_object_v<_T>
+ && same_as<_T, remove_cv_t<_T>>;
+ } // namespace __gen
+ /// @endcond
+
+ template<typename _Ref, typename _V, typename _Alloc>
+ class generator
+ : public ranges::view_interface<generator<_Ref, _V, _Alloc>>
+ {
+ using _Value = __conditional_t<is_void_v<_V>, remove_cvref_t<_Ref>, _V>;
+ static_assert(__gen::_Cv_unqualified_object<_Value>,
+ "Generator value must be a cv-unqualified object type");
+ using _Reference = __gen::_Reference_t<_Ref, _V>;
+ static_assert(is_reference_v<_Reference>
+ || (__gen::_Cv_unqualified_object<_Reference>
+ && copy_constructible<_Reference>),
+ "Generator reference type must be either a cv-unqualified "
+ "object type that is trivially constructible or a "
+ "reference type");
+
+ using _RRef = __conditional_t<
+ is_reference_v<_Reference>,
+ remove_reference_t<_Reference>&&,
+ _Reference>;
+
+ /* Required to model indirectly_readable, and input_iterator. */
+ static_assert(common_reference_with<_Reference&&, _Value&&>);
+ static_assert(common_reference_with<_Reference&&, _RRef&&>);
+ static_assert(common_reference_with<_RRef&&, const _Value&>);
+
+ using _Yielded = __gen::_Yield_t<_Reference>;
+ using _Erased_promise = __gen::_Promise_erased<_Yielded>;
+
+ struct _Iterator;
+
+ friend _Erased_promise;
+ friend struct _Erased_promise::_Subyield_state;
+ public:
+ using yielded = _Yielded;
+
+ struct promise_type : _Erased_promise, __gen::_Promise_alloc<_Alloc>
+ {
+ generator get_return_object() noexcept
+ { return { coroutine_handle<promise_type>::from_promise(*this) }; }
+ };
+
+ static_assert(is_pointer_interconvertible_base_of_v<_Erased_promise,
+ promise_type>);
+
+ generator(const generator&) = delete;
+
+ generator(generator&& __other) noexcept
+ : _M_coro(std::__exchange(__other._M_coro, nullptr)),
+ _M_began(std::__exchange(__other._M_began, false))
+ {}
+
+ ~generator()
+ {
+ if (auto& __c = this->_M_coro)
+ __c.destroy();
+ }
+
+ generator&
+ operator=(generator __other) noexcept
+ {
+ swap(__other._M_coro, this->_M_coro);
+ swap(__other._M_began, this->_M_began);
+ }
+
+ _Iterator
+ begin()
+ {
+ this->_M_mark_as_started();
+ auto __h = _Coro_handle::from_promise(_M_coro.promise());
+ __h.promise()._M_nest._M_top() = __h;
+ return { __h };
+ }
+
+ default_sentinel_t
+ end() const noexcept
+ { return default_sentinel; }
+
+ private:
+ using _Coro_handle = std::coroutine_handle<_Erased_promise>;
+
+ generator(coroutine_handle<promise_type> __coro) noexcept
+ : _M_coro { move(__coro) }
+ {}
+
+ void
+ _M_mark_as_started() noexcept
+ {
+ __glibcxx_assert(!this->_M_began);
+ this->_M_began = true;
+ }
+
+ coroutine_handle<promise_type> _M_coro;
+ bool _M_began = false;
+ };
+
+ template<class _Ref, class _V, class _Alloc>
+ struct generator<_Ref, _V, _Alloc>::_Iterator
+ {
+ using value_type = _Value;
+ using difference_type = ptrdiff_t;
+
+ friend bool
+ operator==(const _Iterator& i, default_sentinel_t) noexcept
+ { return i._M_coro.done(); }
+
+ friend class generator;
+
+ _Iterator(_Iterator&& __o) noexcept
+ : _M_coro(std::__exchange(__o._M_coro, {}))
+ {}
+
+ _Iterator&
+ operator=(_Iterator&& __o) noexcept
+ {
+ this->_M_coro = std::__exchange(__o._M_coro, {});
+ return *this;
+ }
+
+ _Iterator&
+ operator++()
+ {
+ _M_next();
+ return *this;
+ }
+
+ void
+ operator++(int)
+ { this->operator++(); }
+
+ yielded
+ operator*()
+ const noexcept(is_nothrow_move_constructible_v<_Reference>)
+ {
+ auto& __p = this->_M_coro.promise();
+ return static_cast<yielded>(*__p._M_value());
+ }
+
+ private:
+ friend class generator;
+
+ _Iterator(_Coro_handle __g)
+ : _M_coro { __g }
+ { this->_M_next(); }
+
+ void _M_next()
+ {
+ auto& __t = this->_M_coro.promise()._M_nest._M_top();
+ __t.resume();
+ }
+
+ _Coro_handle _M_coro;
+ };
+
+ /// @}
+
+#if _GLIBCXX_HOSTED
+ namespace pmr {
+ template<class R, class V = void>
+ using generator = std::generator<R, V, polymorphic_allocator<std::byte>>;
+ }
+#endif // HOSTED
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace std
+#endif // __cpp_lib_generator
+
+#endif // _GLIBCXX_GENERATOR
@@ -67,6 +67,10 @@
#define __glibcxx_want_ranges_zip
#include <bits/version.h>
+#ifdef __glibcxx_generator // C++ >= 23 && __glibcxx_coroutine
+# include <bits/elements_of.h>
+#endif
+
/**
* @defgroup ranges Ranges
*
new file mode 100644
@@ -0,0 +1,55 @@
+// { dg-do run { target c++23 } }
+// Copyright (C) 2023 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <generator>
+#include <iostream>
+
+// basic example
+std::generator<int>
+bar()
+{
+ co_yield 3;
+ co_yield 4;
+}
+
+std::generator<int>
+foo()
+{
+ co_yield 1;
+ co_yield 2;
+ co_yield std::ranges::elements_of { bar() };
+ co_yield 5;
+}
+
+int
+main()
+{
+ for (auto x : foo())
+ std::cout << x << '\n';
+}
+
+// { dg-output {1(\n|\r\n|\r)} }
+// { dg-output {2(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
+// { dg-output {4(\n|\r\n|\r)} }
+// { dg-output {5(\n|\r\n|\r)} }
new file mode 100644
@@ -0,0 +1,219 @@
+// { dg-do run { target c++23 } }
+// Copyright (C) 2023 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <iostream>
+#include <generator>
+#include <testsuite_hooks.h>
+
+struct foo
+{
+ int id;
+
+ foo(int id)
+ : id { id }
+ {}
+
+ foo(const foo& o)
+ : id { o.id * 100 }
+ {
+ std::cout << "copy-consed " << o.id << "->" << id << '\n';
+ }
+
+ foo&
+ operator=(const foo& o)
+ {
+ id = o.id * 100;
+ std::cout << "copied " << o.id << "->" << id << '\n';
+ return *this;
+ }
+
+ foo(foo&& o)
+ : id { o.id }
+ {
+ o.id = -1;
+ std::cout << "moved " << id << '\n';
+ }
+
+ foo&
+ operator=(foo&& o)
+ {
+ std::swap(o.id, id);
+ std::cout << "swapped " << id << '\n';
+ return *this;
+ }
+};
+
+std::generator<foo>
+foogen()
+{
+ co_yield foo{0};
+
+ {
+ foo f {1};
+ co_yield f;
+ }
+
+ {
+ const foo f {2};
+ co_yield f;
+ }
+
+ {
+ foo f {3};
+ co_yield std::move(f);
+ }
+}
+
+std::generator<const foo&>
+foogen2()
+{
+ co_yield foo{0};
+
+ {
+ foo f {1};
+ co_yield f;
+ }
+
+ {
+ const foo f {2};
+ co_yield f;
+ }
+
+ {
+ foo f {3};
+ co_yield std::move(f);
+ }
+}
+
+std::generator<foo&&>
+foogen3()
+{
+ co_yield foo{0};
+
+ {
+ foo f {1};
+ co_yield f;
+ }
+
+ {
+ const foo f {2};
+ co_yield f;
+ }
+
+ {
+ foo f {3};
+ co_yield std::move(f);
+ }
+}
+
+int
+main()
+{
+ for (auto f : foogen())
+ std::cout << f.id << '\n';
+ for (const auto& f : foogen())
+ std::cout << f.id << '\n';
+ for (auto&& f : foogen())
+ std::cout << f.id << '\n';
+
+ std::cout << "---\n";
+
+ for (auto f : foogen2())
+ std::cout << f.id << '\n';
+ for (const auto& f : foogen2())
+ std::cout << f.id << '\n';
+ for (auto&& f : foogen2())
+ std::cout << f.id << '\n';
+
+ std::cout << "---\n";
+
+ for (auto f : foogen3())
+ std::cout << f.id << '\n';
+ for (const auto& f : foogen3())
+ std::cout << f.id << '\n';
+ for (auto&& f : foogen3())
+ std::cout << f.id << '\n';
+}
+
+// { dg-output {moved 0(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
+// { dg-output {moved 100(\n|\r\n|\r)} }
+// { dg-output {100(\n|\r\n|\r)} }
+// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
+// { dg-output {moved 200(\n|\r\n|\r)} }
+// { dg-output {200(\n|\r\n|\r)} }
+// { dg-output {moved 3(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
+// { dg-output {100(\n|\r\n|\r)} }
+// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
+// { dg-output {200(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
+// { dg-output {100(\n|\r\n|\r)} }
+// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
+// { dg-output {200(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
+// { dg-output {---(\n|\r\n|\r)} }
+// { dg-output {copy-consed 0->0(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
+// { dg-output {100(\n|\r\n|\r)} }
+// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
+// { dg-output {200(\n|\r\n|\r)} }
+// { dg-output {copy-consed 3->300(\n|\r\n|\r)} }
+// { dg-output {300(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {1(\n|\r\n|\r)} }
+// { dg-output {2(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {1(\n|\r\n|\r)} }
+// { dg-output {2(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
+// { dg-output {---(\n|\r\n|\r)} }
+// { dg-output {moved 0(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
+// { dg-output {moved 100(\n|\r\n|\r)} }
+// { dg-output {100(\n|\r\n|\r)} }
+// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
+// { dg-output {moved 200(\n|\r\n|\r)} }
+// { dg-output {200(\n|\r\n|\r)} }
+// { dg-output {moved 3(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
+// { dg-output {100(\n|\r\n|\r)} }
+// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
+// { dg-output {200(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
+// { dg-output {0(\n|\r\n|\r)} }
+// { dg-output {copy-consed 1->100(\n|\r\n|\r)} }
+// { dg-output {100(\n|\r\n|\r)} }
+// { dg-output {copy-consed 2->200(\n|\r\n|\r)} }
+// { dg-output {200(\n|\r\n|\r)} }
+// { dg-output {3(\n|\r\n|\r)} }
new file mode 100644
@@ -0,0 +1,97 @@
+// { dg-do run { target c++23 } }
+// Copyright (C) 2023 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <testsuite_hooks.h>
+#include <generator>
+
+template<unsigned MaxCopies>
+struct copy_max
+{
+ int copy = 0;
+
+ copy_max()
+ {}
+
+ copy_max(const copy_max& o)
+ : copy {o.copy + 1}
+ {
+ VERIFY(copy <= MaxCopies);
+ }
+
+ copy_max&
+ operator=(const copy_max& o)
+ {
+ copy = o.copy + 1;
+ VERIFY(copy <= MaxCopies);
+ return *this;
+ }
+
+ copy_max(copy_max&& o)
+ {
+ std::swap(o.copy, this->copy);
+ }
+
+ copy_max&
+ operator=(copy_max&& o)
+ {
+ std::swap(o.copy, this->copy);
+ return *this;
+ }
+};
+
+template<typename Ref>
+std::generator<Ref>
+foo()
+{
+ co_yield {};
+}
+
+int
+main()
+{
+ static_assert(!std::copy_constructible<std::generator<int>>);
+ {
+ auto gen = foo<const copy_max<1>&>();
+ auto i = gen.begin();
+ *i;
+ *i;
+ auto is = *i;
+ VERIFY(is.copy > 0);
+ }
+
+ {
+ auto gen2 = foo<copy_max<0>&&>();
+ auto i = gen2.begin();
+ *i;
+ *i;
+ auto is = *i;
+ }
+
+ {
+ auto gen = foo<copy_max<0>>(); // should be same as case 2
+ auto i = gen.begin();
+ *i;
+ *i;
+ auto is = *i;
+ }
+}
new file mode 100644
@@ -0,0 +1,97 @@
+// { dg-do run { target c++23 } }
+// Copyright (C) 2023 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <testsuite_hooks.h>
+#include <generator>
+
+std::generator<int>
+foo()
+{
+ co_yield 0;
+ throw 3; /* dice roll */
+}
+
+std::generator<int>
+foo_delegator()
+{
+ co_yield 1;
+ co_yield std::ranges::elements_of { foo() };
+}
+
+bool catchy_caught = false;
+
+std::generator<int>
+foo_catchy_delegator()
+{
+ try
+ {
+ co_yield std::ranges::elements_of { foo() };
+ VERIFY(false);
+ }
+ catch (int i)
+ {
+ catchy_caught = true;
+ VERIFY(i == 3);
+ }
+}
+
+int
+main()
+{
+ {
+ auto gen = foo();
+ try
+ {
+ auto it = gen.begin();
+ VERIFY(*it == 0);
+ it++;
+ VERIFY(false);
+ }
+ catch (int x)
+ {
+ VERIFY(x == 3);
+ }
+ }
+
+ {
+ auto gen = foo_delegator();
+ auto it = gen.begin();
+ VERIFY(*it == 1);
+ it++;
+
+ try
+ {
+ VERIFY(*it == 0);
+ it++;
+ VERIFY(false);
+ }
+ catch (int x)
+ {
+ VERIFY(x == 3);
+ }
+ }
+
+ for (auto x : foo_catchy_delegator())
+ VERIFY(x == 0);
+ VERIFY(catchy_caught);
+}
new file mode 100644
@@ -0,0 +1,45 @@
+// { dg-do run { target c++23 } }
+// Copyright (C) 2023 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <generator>
+#include <ranges>
+#include <vector>
+#include <iostream>
+
+std::generator<int&>
+yield_vector()
+{
+ std::vector foo { 1, 2, 3 };
+ auto x = 123;
+ co_yield x;
+ co_yield std::ranges::elements_of { foo };
+ x = 456;
+ co_yield x;
+}
+
+int
+main()
+{
+ for (auto x : yield_vector())
+ std::cout << x << '\n';
+}
new file mode 100644
@@ -0,0 +1,38 @@
+// { dg-do compile { target c++23 } }
+// { dg-add-options no_pch }
+// Copyright (C) 2023 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library. This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
+// <http://www.gnu.org/licenses/>.
+
+#include <generator>
+
+#if !defined(__cpp_lib_generator) || __cpp_lib_generator < 202207L
+# error "__cpp_lib_generator undefined or has wrong value"
+#endif
+
+namespace test {
+ using std::generator;
+#if __STDC_HOSTED__
+ namespace pmr {
+ using std::pmr::generator;
+ }
+#endif
+}