From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id CBBAD386C5BA for ; Thu, 21 Dec 2023 21:51:19 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org CBBAD386C5BA Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org CBBAD386C5BA Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.129.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1703195485; cv=none; b=gN8NhFAxkLlTXxoQuR/yuG2lPOc1OPsD9JyFoghunlyiq54f868TXb53amOrXWJwyqJN9K3oAFXU61T/Z44Mk/IfI4evXysz0gwWA1EmqRt7Xzg25nzfWskSa3VAVsXXt3ntnqhidRvrxtyf/Xv29OnH+fzOU3Zwqmrk96/HlEw= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1703195485; c=relaxed/simple; bh=tpfeaVPOqd31DBt8W6Ay4lCBONDnyyYpniKUqz9t9d0=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=N7y6fDWc9dP2sqVk6QwxXSpD1bodAY4CaHMxo0K4JbECNTuKk92vUsrieIyZXzuPVUsgRbbMA04De3MP/B13U6zywGKjuzi9h14+O0KDl5dqmKQV3a+KysnMZ1B42TQcD5V8U5Brfpg5EKaaqzFUjgdI6OUfmfhcT11W4nXCGos= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1703195479; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=hqwIGV0pBQEk7sByuINPmQJhJ4vY7E7Y2bnfhD6aUBw=; b=QX2wJa7dC1WlDau5YuRXvFRl+Q3zPj8O7QFAzNGOH1Maq39hIj7GGKSVlgsXq1E7YjzhT0 +9LHcuQlpfsi5Mx/EicQThgQCqu5d9MnXpYGF/eV6BU03VU8wqZsrD1SRIAKpshP7gGYRh Rb48Gzeiko0CaGIvtNMy3/nmcZaAk/w= Received: from mail-yw1-f197.google.com (mail-yw1-f197.google.com [209.85.128.197]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-677-dGNlMFsKNIK6FwOp3Xl7uw-1; Thu, 21 Dec 2023 16:51:17 -0500 X-MC-Unique: dGNlMFsKNIK6FwOp3Xl7uw-1 Received: by mail-yw1-f197.google.com with SMTP id 00721157ae682-5e20c9c4080so23133277b3.3 for ; Thu, 21 Dec 2023 13:51:17 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1703195477; x=1703800277; h=content-transfer-encoding:cc:to:subject:message-id:date:from :in-reply-to:references:mime-version:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hqwIGV0pBQEk7sByuINPmQJhJ4vY7E7Y2bnfhD6aUBw=; b=LlfCEbIaigiMreKK9aSZ5i9rsZ7T/eE7b2Cra3PFccW9z5IayyoCXpzaLjk2yRu0zT Qgm7rxOJAT71Qr0NdlxSMV/qxi0LXnNZG13uhwFVPzi+2mjddd4yOUrqUCHP5tm5QdYo nHHNGvlvc+zbZ8mcTqVnHHpa6dWGzLxLhmZpiC1jQSJNgMBGU37V+gmzwcKhtYMkd7IV kmRHUnBz6p9gfT0rdOfu/1POAgEDOnZaJsm7BN36p1xRz/oGxJmcYsFhViQcEbSRGuIY VrSBCRA3Hpw1s5PywWF7aaz00BumOY8kUlMqvBvJih+AMrNXlHDv57902xHqOGxamSWZ eglQ== X-Gm-Message-State: AOJu0Yx8AdCTMjJs9gbR2yF9oeAocZZwiQ4dExLS71JLwy6hc5UL6qyD 9XlwDJelOeWWLWFYIQreG3Uz9kEc22Zdzayd9AanN/teFKRYpxRxELcEp5T7DCmaYkh98EBUfki /THUWK/Z20naveEudX6j0w1vqi4qPBn0wglOI2hUq2sCBB18= X-Received: by 2002:a0d:d851:0:b0:5e3:f76:dcd6 with SMTP id a78-20020a0dd851000000b005e30f76dcd6mr495915ywe.56.1703195476852; Thu, 21 Dec 2023 13:51:16 -0800 (PST) X-Google-Smtp-Source: AGHT+IHlSzZ7hQxCnJGejlv2678hMbuJXHw6ix/PXI7nvpSJqknwPJrfvTiUKYhnFdgm92PyVm6OciaB17mK8Mp7YLA= X-Received: by 2002:a0d:d851:0:b0:5e3:f76:dcd6 with SMTP id a78-20020a0dd851000000b005e30f76dcd6mr495910ywe.56.1703195476212; Thu, 21 Dec 2023 13:51:16 -0800 (PST) MIME-Version: 1.0 References: <20231221211909.371736-1-arsen@aarsen.me> <20231221211909.371736-3-arsen@aarsen.me> In-Reply-To: <20231221211909.371736-3-arsen@aarsen.me> From: Jonathan Wakely Date: Thu, 21 Dec 2023 21:51:00 +0000 Message-ID: Subject: Re: [PATCH v2 2/2] libstdc++: implement std::generator To: =?UTF-8?Q?Arsen_Arsenovi=C4=87?= Cc: gcc-patches@gcc.gnu.org, libstdc++@gcc.gnu.org X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00,DKIMWL_WL_HIGH,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H4,RCVD_IN_MSPIKE_WL,SPF_HELO_NONE,SPF_NONE,TXREP,T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: On Thu, 21 Dec 2023 at 21:26, Arsen Arsenovi=C4=87 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 . > * 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. OK > --- > 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 | 812 ++++++++++++++++++ > 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, 1464 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/Make= file.am > index 368b92eafbc7..ca76afbcc77f 100644 > --- a/libstdc++-v3/include/Makefile.am > +++ b/libstdc++-v3/include/Makefile.am > @@ -35,6 +35,7 @@ std_freestanding =3D \ > ${std_srcdir}/coroutine \ > ${std_srcdir}/expected \ > ${std_srcdir}/functional \ > + ${std_srcdir}/generator \ > ${std_srcdir}/iterator \ > ${std_srcdir}/limits \ > ${std_srcdir}/memory \ > @@ -123,6 +124,7 @@ bits_freestanding =3D \ > ${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/Make= file.in > index a31588c01002..4fa4a259fef3 100644 > --- a/libstdc++-v3/include/Makefile.in > +++ b/libstdc++-v3/include/Makefile.in > @@ -393,6 +393,7 @@ std_freestanding =3D \ > ${std_srcdir}/coroutine \ > ${std_srcdir}/expected \ > ${std_srcdir}/functional \ > + ${std_srcdir}/generator \ > ${std_srcdir}/iterator \ > ${std_srcdir}/limits \ > ${std_srcdir}/memory \ > @@ -478,6 +479,7 @@ bits_freestanding =3D \ > ${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/inclu= de/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 -*- 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 > +// . > + > +#ifndef _GLIBCXX_BITS_ELEMENTS_OF > +#define _GLIBCXX_BITS_ELEMENTS_OF > + > +#pragma GCC system_header > + > +#include > + > +#include > + > +// C++ >=3D 23 && __glibcxx_coroutine > +#if defined(__glibcxx_ranges) && defined(__glibcxx_generator) > +#include > +#include > + > +#if _GLIBCXX_HOSTED > +# include // likely desirable if hosted. > +#endif // HOSTED > + > +namespace std _GLIBCXX_VISIBILITY(default) > +{ > +_GLIBCXX_BEGIN_NAMESPACE_VERSION > +namespace ranges > +{ > + > + /** > + * @ingroup ranges > + * @since C++23 > + * @{ > + */ > + > + template> > + struct elements_of > + { > + [[no_unique_address]] _Range range; > + [[no_unique_address]] _Alloc allocator =3D _Alloc(); > + }; > + > + template> > + elements_of(_Range&&, _Alloc =3D _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 0134a71b3ab4..71cb75dd07a7 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1743,6 +1743,15 @@ ftms =3D { > }; > }; > > +ftms =3D { > + name =3D generator; > + values =3D { > + v =3D 202207; > + cxxmin =3D 23; > + extra_cond =3D "__glibcxx_coroutine"; > + }; > +}; > + > // Standard test specifications. > stds[97] =3D ">=3D 199711L"; > stds[03] =3D ">=3D 199711L"; > diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/b= its/version.h > index c28fbe14c15f..bbbeac8087b3 100644 > --- a/libstdc++-v3/include/bits/version.h > +++ b/libstdc++-v3/include/bits/version.h > @@ -2125,4 +2125,15 @@ > #endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_str= ing) */ > #undef __glibcxx_want_to_string > > +// from version.def line 1637 > +#if !defined(__cpp_lib_generator) > +# if (__cplusplus >=3D 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_genera= tor) */ > +#undef __glibcxx_want_generator > + > #undef __glibcxx_want_all > diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/inc= lude/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 > +#include > #include > #if __has_include() > # include > diff --git a/libstdc++-v3/include/std/generator b/libstdc++-v3/include/st= d/generator > new file mode 100644 > index 000000000000..e438fae4b981 > --- /dev/null > +++ b/libstdc++-v3/include/std/generator > @@ -0,0 +1,812 @@ > +// -*- 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 > +// . > + > +/** @file include/generator > + * This is a Standard C++ Library header. > + */ > + > +#ifndef _GLIBCXX_GENERATOR > +#define _GLIBCXX_GENERATOR > + > +#include > +#pragma GCC system_header > + > +#include > + > +#define __glibcxx_want_generator > +#include > + > +#ifdef __cpp_lib_generator // C++ >=3D 23 && __glibcxx_coroutine > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > + > +#if _GLIBCXX_HOSTED > +# include > +#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 corou= tines > + * that yield elements as a range. Generator coroutines are synchrono= us. > + * > + * @headerfile generator > + * @since C++23 > + */ > + template > + 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 > + using _Reference_t =3D __conditional_t, > + _Ref&&, _Ref>; > + > + /// Type yielded by a generator whose _Reference type is _Reference. > + template > + using _Yield_t =3D __conditional_t, > + _Reference, > + const _Reference&>; > + > + /// _Yield_t * _Reference_t > + template > + using _Yield2_t =3D _Yield_t<_Reference_t<_Ref, _Val>>; > + > + template constexpr bool __is_generator =3D false; > + template > + constexpr bool __is_generator> = =3D true; > + > + /// Allocator and value type erased generator promise type. > + /// \tparam _Yielded The corresponding generators yielded type. > + template > + class _Promise_erased > + { > + static_assert(is_reference_v<_Yielded>); > + using _Yielded_deref =3D remove_reference_t<_Yielded>; > + using _Yielded_decvref =3D remove_cvref_t<_Yielded>; > + using _ValuePtr =3D add_pointer_t<_Yielded>; > + using _Coro_handle =3D std::coroutine_handle<_Promise_erased>; > + > + template > + friend class std::generator; > + > + template > + struct _Recursive_awaiter; > + template > + 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() =3D ::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 > + requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded> > + auto > + yield_value(ranges::elements_of&&, _U2> = __r) > + noexcept > + { return _Recursive_awaiter { std::move(__r.range) }; } > + > + template > + requires convertible_to, _Yielded> > + auto > + yield_value(ranges::elements_of<_R, _Alloc> __r) > + noexcept > + { > + auto __n =3D [] (allocator_arg_t, _Alloc, > + ranges::iterator_t<_R> __i, > + ranges::sentinel_t<_R> __s) > + -> generator<_Yielded, ranges::range_value_t<_R>, _Alloc> { > + for (; __i !=3D __s; ++__i) > + co_yield static_cast<_Yielded>(*__i); > + }; > + return yield_value(ranges::elements_of(__n(allocator_arg, > + __r.allocator, > + ranges::begin(__r.ra= nge), > + ranges::end(__r.rang= e)))); > + } > + > + > + _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 coroutin= e is > + // the sole entry of the stack iff it is both the top and the b= ottom. 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 =3D std::current_exception(); > + } > + > + void await_transform() =3D 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 > + 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 =3D 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 =3D std::get_if<_Frame>(&this->_M_stack)) > + return __f->_M_bottom.promise()._M_nest._M_top(); > + > + auto __bf =3D 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 =3D=3D this); > + __glibcxx_assert(this->_M_top() =3D=3D __current); > + > + __subyield.promise()._M_nest._M_jump_in(__current, __subyield); > + } > + > + std::coroutine_handle<> > + _M_pop() noexcept > + { > + if (auto __f =3D std::get_if<_Frame>(&this->_M_stack)) > + { > + // We aren't a bottom coroutine. Restore the parent to the= top > + // and resume. > + auto __p =3D this->_M_top() =3D __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 =3D=3D this); > + __glibcxx_assert(this->_M_is_bottom()); > + // We're bottom. We're also top of top is unset (note that thi= s is > + // not true if something was added to the coro stack and then p= opped, > + // but in that case we can't possibly be yielded from, as it wo= uld > + // require rerunning begin()). > + __glibcxx_assert(!this->_M_top()); > + > + auto& __rn =3D __rest.promise()._M_nest; > + __rn._M_top() =3D __new; > + > + // Presume we're the second frame... > + auto __bott =3D __rest; > + if (auto __f =3D 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 h= ave a > + // _Frame member. > + __bott =3D __f->_M_bottom; > + > + this->_M_stack =3D _Frame { > + ._M_bottom =3D __bott, > + ._M_parent =3D __rest > + }; > + } > + > + _ValuePtr& > + _M_bottom_value(_Promise_erased& __current) noexcept > + { > + __glibcxx_assert(&__current._M_nest =3D=3D this); > + if (auto __bf =3D std::get_if<_Bottom_frame>(&this->_M_stack)) > + return __bf->_M_value; > + auto __f =3D std::get_if<_Frame>(&this->_M_stack); > + __glibcxx_assert(__f); > + auto& __p =3D __f->_M_bottom.promise(); > + return __p._M_nest._M_value(__p); > + } > + > + _ValuePtr& > + _M_value(_Promise_erased& __current) noexcept > + { > + __glibcxx_assert(&__current._M_nest =3D=3D this); > + auto __bf =3D std::get_if<_Bottom_frame>(&this->_M_stack); > + __glibcxx_assert(__bf); > + return __bf->_M_value; > + } > + }; > + > + template > + struct _Promise_erased<_Yielded>::_Final_awaiter > + { > + bool await_ready() noexcept > + { return false; } > + > + template > + auto await_suspend(std::coroutine_handle<_Promise> __c) noexcept > + { > + static_assert(is_pointer_interconvertible_base_of_v< > + _Promise_erased, _Promise>); > + > + auto& __n =3D __c.promise()._M_nest; > + return __n._M_pop(); > + } > + > + void await_resume() noexcept {} > + }; > + > + template > + struct _Promise_erased<_Yielded>::_Copy_awaiter > + { > + _Yielded_decvref _M_value; > + _ValuePtr& _M_bottom_value; > + > + constexpr bool await_ready() noexcept > + { return false; } > + > + template > + void await_suspend(std::coroutine_handle<_Promise>) noexcept > + { > + static_assert(is_pointer_interconvertible_base_of_v< > + _Promise_erased, _Promise>); > + _M_bottom_value =3D ::std::addressof(_M_value); > + } > + > + constexpr void > + await_resume() const noexcept > + {} > + }; > + > + template > + template > + struct _Promise_erased<_Yielded>::_Recursive_awaiter > + { > + _Gen _M_gen; > + static_assert(__is_generator<_Gen>); > + static_assert(std::same_as); > + > + _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 > + std::coroutine_handle<> > + await_suspend(std::coroutine_handle<_Promise> __p) noexcept > + { > + static_assert(is_pointer_interconvertible_base_of_v< > + _Promise_erased, _Promise>); > + > + auto __c =3D _Coro_handle::from_address(__p.address()); > + auto __t =3D _Coro_handle::from_address(this->_M_gen._M_coro.ad= dress()); > + __p.promise()._M_nest._M_push(__c, __t); > + return __t; > + } > + > + void await_resume() > + { > + if (auto __e =3D _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 =3D sizeof(_Alloc_block); > + return (__sz + __blksz - 1) / __blksz; > + } > + }; > + > + template > + concept _Stateless_alloc =3D (allocator_traits<_A>::is_always_equal:= :value > + && default_initializable<_A>); > + > + template > + class _Promise_alloc > + { > + using _ATr =3D allocator_traits<_Alloc>; > + using _Rebound =3D typename _ATr::template rebind_alloc<_Alloc_bl= ock>; > + using _Rebound_ATr =3D typename _ATr > + ::template rebind_traits<_Alloc_block>; > + static_assert(is_pointer_v, > + "Must use allocators for true pointers with generat= ors"); > + > + static auto > + _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexc= ept > + { > + auto __an =3D __fn + __fsz; > + auto __ba =3D alignof(_Rebound); > + return reinterpret_cast<_Rebound*>(((__an + __ba - 1) / __ba) *= __ba); > + } > + > + static auto > + _M_alloc_size(std::size_t __csz) noexcept > + { > + auto __ba =3D 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 =3D _Alloc_block::_M_cnt(_M_alloc_size(__csz)); > + auto __f =3D __b.allocate(__nsz); > + auto __fn =3D reinterpret_cast(__f); > + auto __an =3D _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 > + void* > + operator new(std::size_t __sz, > + allocator_arg_t, const _Na& __na, > + const _Args&...) > + requires convertible_to > + { > + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__= na)), > + __sz); > + } > + > + template > + void* > + operator new(std::size_t __sz, > + const _This&, > + allocator_arg_t, const _Na& __na, > + const _Args&...) > + requires convertible_to > + { > + 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 =3D _Alloc_block::_M_cnt(_M_alloc_size(__csz)); > + auto __fn =3D reinterpret_cast(__ptr); > + auto __an =3D _M_alloc_address(__fn, __csz); > + _Rebound __b(std::move(*__an)); > + __an->~_Rebound(); > + __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __ns= z); > + } > + } > + }; > + > + template<> > + class _Promise_alloc > + { > + using _Dealloc_fn =3D void (*)(void*, std::size_t); > + > + static auto > + _M_dealloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noe= xcept > + { > + auto __an =3D __fn + __fsz; > + auto __ba =3D alignof(_Dealloc_fn); > + auto __aligned =3D ((__an + __ba - 1) / __ba) * __ba; > + return reinterpret_cast<_Dealloc_fn*>(__aligned); > + } > + > + template > + static auto > + _M_alloc_address(std::uintptr_t __fn, std::uintptr_t __fsz) noexc= ept > + requires (!_Stateless_alloc<_Rebound>) > + { > + auto __ba =3D alignof(_Rebound); > + auto __da =3D _M_dealloc_address(__fn, __fsz); > + auto __aan =3D reinterpret_cast(__da); > + __aan +=3D sizeof(_Dealloc_fn); > + auto __aligned =3D ((__aan + __ba - 1) / __ba) * __ba; > + return reinterpret_cast<_Rebound*>(__aligned); > + } > + > + template > + 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 =3D 0; > + std::size_t __as =3D 0; > + if constexpr (!std::same_as<_Rebound, void>) > + { > + __aa =3D alignof(_Rebound); > + __as =3D sizeof(_Rebound); > + } > + auto __ba =3D __aa + alignof(_Dealloc_fn); > + return __csz + __ba + __as + sizeof(_Dealloc_fn); > + } > + > + template > + static void > + _M_deallocator(void* __ptr, std::size_t __csz) noexcept > + { > + auto __asz =3D _M_alloc_size<_Rebound>(__csz); > + auto __nblk =3D _Alloc_block::_M_cnt(__asz); > + > + if constexpr (_Stateless_alloc<_Rebound>) > + { > + _Rebound __b; > + __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nb= lk); > + } > + else > + { > + auto __fn =3D reinterpret_cast(__ptr); > + auto __an =3D _M_alloc_address<_Rebound>(__fn, __csz); > + _Rebound __b(std::move(*__an)); > + __an->~_Rebound(); > + __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), __nb= lk); > + } > + } > + > + template > + static void* > + _M_allocate(const _Na& __na, std::size_t __csz) > + { > + using _Rebound =3D typename std::allocator_traits<_Na> > + ::template rebind_alloc<_Alloc_block>; > + using _Rebound_ATr =3D typename std::allocator_traits<_Na> > + ::template rebind_traits<_Alloc_block>; > + > + static_assert(is_pointer_v, > + "Must use allocators for true pointers with gener= ators"); > + > + _Dealloc_fn __d =3D &_M_deallocator<_Rebound>; > + auto __b =3D static_cast<_Rebound>(__na); > + auto __asz =3D _M_alloc_size<_Rebound>(__csz); > + auto __nblk =3D _Alloc_block::_M_cnt(__asz); > + void* __p =3D __b.allocate(__nblk); > + auto __pn =3D reinterpret_cast(__p); > + *_M_dealloc_address(__pn, __csz) =3D __d; > + if constexpr (!_Stateless_alloc<_Rebound>) > + { > + auto __an =3D _M_alloc_address<_Rebound>(__pn, __csz); > + ::new (__an) _Rebound(std::move(__b)); > + } > + return __p; > + } > + public: > + void* > + operator new(std::size_t __sz) > + { > + auto __nsz =3D _M_alloc_size(__sz); > + _Dealloc_fn __d =3D [] (void* __ptr, std::size_t __sz) > + { > + ::operator delete(__ptr, _M_alloc_size(__sz)); > + }; > + auto __p =3D ::operator new(__nsz); > + auto __pn =3D reinterpret_cast(__p); > + *_M_dealloc_address(__pn, __sz) =3D __d; > + return __p; > + } > + > + template > + void* > + operator new(std::size_t __sz, > + allocator_arg_t, const _Na& __na, > + const _Args&...) > + { return _M_allocate(__na, __sz); } > + > + template > + 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 =3D reinterpret_cast(__ptr); > + __d =3D *_M_dealloc_address(__pn, __sz); > + __d(__ptr, __sz); > + } > + }; > + > + template > + concept _Cv_unqualified_object =3D is_object_v<_Tp> > + && same_as<_Tp, remove_cv_t<_Tp>>; > + } // namespace __gen > + /// @endcond > + > + template > + class generator > + : public ranges::view_interface> > + { > + using _Value =3D __conditional_t, remove_cvref_t<_Re= f>, _V>; > + static_assert(__gen::_Cv_unqualified_object<_Value>, > + "Generator value must be a cv-unqualified object type= "); > + using _Reference =3D __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-unquali= fied " > + "object type that is trivially constructible or a " > + "reference type"); > + > + using _RRef =3D __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 =3D __gen::_Yield_t<_Reference>; > + using _Erased_promise =3D __gen::_Promise_erased<_Yielded>; > + > + struct _Iterator; > + > + friend _Erased_promise; > + friend struct _Erased_promise::_Subyield_state; > + public: > + using yielded =3D _Yielded; > + > + struct promise_type : _Erased_promise, __gen::_Promise_alloc<_Allo= c> > + { > + generator get_return_object() noexcept > + { return { coroutine_handle::from_promise(*this) };= } > + }; > + > + static_assert(is_pointer_interconvertible_base_of_v<_Erased_promis= e, > + promise_type>); > + > + generator(const generator&) =3D 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 =3D this->_M_coro) > + __c.destroy(); > + } > + > + generator& > + operator=3D(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 =3D _Coro_handle::from_promise(_M_coro.promise()); > + __h.promise()._M_nest._M_top() =3D __h; > + return { __h }; > + } > + > + default_sentinel_t > + end() const noexcept > + { return default_sentinel; } > + > + private: > + using _Coro_handle =3D std::coroutine_handle<_Erased_promise>; > + > + generator(coroutine_handle __coro) noexcept > + : _M_coro { move(__coro) } > + {} > + > + void > + _M_mark_as_started() noexcept > + { > + __glibcxx_assert(!this->_M_began); > + this->_M_began =3D true; > + } > + > + coroutine_handle _M_coro; > + bool _M_began =3D false; > + }; > + > + template > + struct generator<_Ref, _V, _Alloc>::_Iterator > + { > + using value_type =3D _Value; > + using difference_type =3D ptrdiff_t; > + > + friend bool > + operator=3D=3D(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=3D(_Iterator&& __o) noexcept > + { > + this->_M_coro =3D 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 =3D this->_M_coro.promise(); > + return static_cast(*__p._M_value()); > + } > + > + private: > + friend class generator; > + > + _Iterator(_Coro_handle __g) > + : _M_coro { __g } > + { this->_M_next(); } > + > + void _M_next() > + { > + auto& __t =3D this->_M_coro.promise()._M_nest._M_top(); > + __t.resume(); > + } > + > + _Coro_handle _M_coro; > + }; > + > + /// @} > + > +#if _GLIBCXX_HOSTED > + namespace pmr { > + template > + using generator =3D std::generator<_Ref, _Val, polymorphic_allocator= >; > + } > +#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/r= anges > index 752a04e01bd5..a2e8773a16f6 100644 > --- a/libstdc++-v3/include/std/ranges > +++ b/libstdc++-v3/include/std/ranges > @@ -68,6 +68,10 @@ > #define __glibcxx_want_ranges_zip > #include > > +#ifdef __glibcxx_generator // C++ >=3D 23 && __glibcxx_coroutine > +# include > +#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 > +// . > + > +#include > +#include > + > +// basic example > +std::generator > +bar() > +{ > + co_yield 3; > + co_yield 4; > +} > + > +std::generator > +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 > +// . > + > +#include > +#include > +#include > + > +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=3D(const foo& o) > + { > + id =3D o.id * 100; > + std::cout << "copied " << o.id << "->" << id << '\n'; > + return *this; > + } > + > + foo(foo&& o) > + : id { o.id } > + { > + o.id =3D -1; > + std::cout << "moved " << id << '\n'; > + } > + > + foo& > + operator=3D(foo&& o) > + { > + std::swap(o.id, id); > + std::cout << "swapped " << id << '\n'; > + return *this; > + } > +}; > + > +std::generator > +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 > +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 > +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 > +// . > + > +#include > +#include > + > +template > +struct copy_max > +{ > + int copy =3D 0; > + > + copy_max() > + {} > + > + copy_max(const copy_max& o) > + : copy {o.copy + 1} > + { > + VERIFY(copy <=3D MaxCopies); > + } > + > + copy_max& > + operator=3D(const copy_max& o) > + { > + copy =3D o.copy + 1; > + VERIFY(copy <=3D MaxCopies); > + return *this; > + } > + > + copy_max(copy_max&& o) > + { > + std::swap(o.copy, this->copy); > + } > + > + copy_max& > + operator=3D(copy_max&& o) > + { > + std::swap(o.copy, this->copy); > + return *this; > + } > +}; > + > +template > +std::generator > +foo() > +{ > + co_yield {}; > +} > + > +int > +main() > +{ > + static_assert(!std::copy_constructible>); > + { > + auto gen =3D foo&>(); > + auto i =3D gen.begin(); > + *i; > + *i; > + auto is =3D *i; > + VERIFY(is.copy > 0); > + } > + > + { > + auto gen2 =3D foo&&>(); > + auto i =3D gen2.begin(); > + *i; > + *i; > + auto is =3D *i; > + } > + > + { > + auto gen =3D foo>(); // should be same as case 2 > + auto i =3D gen.begin(); > + *i; > + *i; > + auto is =3D *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 > +// . > + > +#include > +#include > + > +std::generator > +foo() > +{ > + co_yield 0; > + throw 3; /* dice roll */ > +} > + > +std::generator > +foo_delegator() > +{ > + co_yield 1; > + co_yield std::ranges::elements_of { foo() }; > +} > + > +bool catchy_caught =3D false; > + > +std::generator > +foo_catchy_delegator() > +{ > + try > + { > + co_yield std::ranges::elements_of { foo() }; > + VERIFY(false); > + } > + catch (int i) > + { > + catchy_caught =3D true; > + VERIFY(i =3D=3D 3); > + } > +} > + > +int > +main() > +{ > + { > + auto gen =3D foo(); > + try > + { > + auto it =3D gen.begin(); > + VERIFY(*it =3D=3D 0); > + it++; > + VERIFY(false); > + } > + catch (int x) > + { > + VERIFY(x =3D=3D 3); > + } > + } > + > + { > + auto gen =3D foo_delegator(); > + auto it =3D gen.begin(); > + VERIFY(*it =3D=3D 1); > + it++; > + > + try > + { > + VERIFY(*it =3D=3D 0); > + it++; > + VERIFY(false); > + } > + catch (int x) > + { > + VERIFY(x =3D=3D 3); > + } > + } > + > + for (auto x : foo_catchy_delegator()) > + VERIFY(x =3D=3D 0); > + VERIFY(catchy_caught); > +} > diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/subrang= e.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 > +// . > + > +#include > +#include > +#include > +#include > + > +std::generator > +yield_vector() > +{ > + std::vector foo { 1, 2, 3 }; > + auto x =3D 123; > + co_yield x; > + co_yield std::ranges::elements_of { foo }; > + x =3D 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/synopsi= s.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 > +// . > + > +#include > + > +#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.43.0 >