* [PATCH v2 0/2] libstdc++: generators v2 @ 2023-12-21 20:01 Arsen Arsenović 2023-12-21 20:01 ` [PATCH v2 1/2] libstdc++: add missing include in ranges_util.h Arsen Arsenović 2023-12-21 20:01 ` [PATCH v2 2/2] libstdc++: implement std::generator Arsen Arsenović 0 siblings, 2 replies; 10+ messages in thread From: Arsen Arsenović @ 2023-12-21 20:01 UTC (permalink / raw) To: gcc-patches; +Cc: libstdc++, Arsen Arsenović Hi, This is v2 of my generators patch. It addresses Jonathans review comments, but does not add more tests yet :-/ Original series: https://inbox.sourceware.org/20231118195008.579211-1-arsen@aarsen.me/ Changes since v1: - Uglify some symbols - Convert _Is_generator concept to __is_generator CE bool - Add "libstdc++: add missing include in ranges_util.h" - this can be pushed separately, really, but I forgot to send it. Range-diff: 1: feab374887e5 = 1212: c4286af0c70f libstdc++: add missing include in ranges_util.h 2: 010eab271755 ! 1213: fb589641656f libstdc++: implement std::generator @@ libstdc++-v3/include/std/generator (new) +#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> @@ libstdc++-v3/include/std/generator (new) + { + /// _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>, ++ template<typename _Ref, typename _Val> ++ using _Reference_t = __conditional_t<is_void_v<_Val>, + _Ref&&, _Ref>; + + /// Type yielded by a generator whose _Reference type is _Reference. @@ libstdc++-v3/include/std/generator (new) + 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; ++ template<typename _Ref, typename _Val> ++ using _Yield2_t = _Yield_t<_Reference_t<_Ref, _Val>>; + ++ template<typename> constexpr bool __is_generator = false; ++ template<typename _Val, typename _Ref, typename _Alloc> ++ constexpr bool __is_generator<std::generator<_Val, _Ref, _Alloc>> = true; + + /// Allocator and value type erased generator promise type. + /// \tparam _Yielded The corresponding generators yielded type. @@ libstdc++-v3/include/std/generator (new) + using _Coro_handle = std::coroutine_handle<_Promise_erased>; + + template<typename, typename, typename> -+ friend struct std::generator; ++ friend class std::generator; + + template<typename _Gen> + struct _Recursive_awaiter; @@ libstdc++-v3/include/std/generator (new) + __rn._M_top() = __new; + + // Presume we're the second frame... -+ auto& __bott = __rest; ++ 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 @@ libstdc++-v3/include/std/generator (new) + struct _Promise_erased<_Yielded>::_Recursive_awaiter + { + _Gen _M_gen; -+ static_assert(_Is_generator<_Gen>); ++ static_assert(__is_generator<_Gen>); + static_assert(std::same_as<typename _Gen::yielded, _Yielded>); + + _Recursive_awaiter(_Gen __gen) noexcept @@ libstdc++-v3/include/std/generator (new) + requires default_initializable<_Rebound> // _Alloc is non-void + { return _M_allocate({}, __sz); } + -+ template<typename _NA, typename... _Args> ++ template<typename _Na, typename... _Args> + void* + operator new(std::size_t __sz, -+ allocator_arg_t, const _NA& __na, ++ allocator_arg_t, const _Na& __na, + const _Args&...) -+ requires convertible_to<const _NA&, _Alloc> ++ requires convertible_to<const _Na&, _Alloc> + { + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)), + __sz); + } + -+ template<typename _This, typename _NA, typename... _Args> ++ template<typename _This, typename _Na, typename... _Args> + void* + operator new(std::size_t __sz, + const _This&, -+ allocator_arg_t, const _NA& __na, ++ allocator_arg_t, const _Na& __na, + const _Args&...) -+ requires convertible_to<const _NA&, _Alloc> ++ requires convertible_to<const _Na&, _Alloc> + { + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)), + __sz); @@ libstdc++-v3/include/std/generator (new) + } + } + -+ template<typename _NA> ++ template<typename _Na> + static void* -+ _M_allocate(const _NA& __na, std::size_t __csz) ++ _M_allocate(const _Na& __na, std::size_t __csz) + { -+ using _Rebound = typename std::allocator_traits<_NA> ++ using _Rebound = typename std::allocator_traits<_Na> + ::template rebind_alloc<_Alloc_block>; -+ using _Rebound_ATr = typename std::allocator_traits<_NA> ++ using _Rebound_ATr = typename std::allocator_traits<_Na> + ::template rebind_traits<_Alloc_block>; + + static_assert(is_pointer_v<typename _Rebound_ATr::pointer>, @@ libstdc++-v3/include/std/generator (new) + return __p; + } + -+ template<typename _NA, typename... _Args> ++ template<typename _Na, typename... _Args> + void* + operator new(std::size_t __sz, -+ allocator_arg_t, const _NA& __na, ++ allocator_arg_t, const _Na& __na, + const _Args&...) + { return _M_allocate(__na, __sz); } + -+ template<typename _This, typename _NA, typename... _Args> ++ template<typename _This, typename _Na, typename... _Args> + void* + operator new(std::size_t __sz, + const _This&, -+ allocator_arg_t, const _NA& __na, ++ allocator_arg_t, const _Na& __na, + const _Args&...) + { return _M_allocate(__na, __sz); } + @@ libstdc++-v3/include/std/generator (new) + } + }; + -+ template<typename _T> -+ concept _Cv_unqualified_object = is_object_v<_T> -+ && same_as<_T, remove_cv_t<_T>>; ++ template<typename _Tp> ++ concept _Cv_unqualified_object = is_object_v<_Tp> ++ && same_as<_Tp, remove_cv_t<_Tp>>; + } // namespace __gen + /// @endcond + @@ libstdc++-v3/include/std/generator (new) + using difference_type = ptrdiff_t; + + friend bool -+ operator==(const _Iterator& i, default_sentinel_t) noexcept -+ { return i._M_coro.done(); } ++ operator==(const _Iterator& __i, default_sentinel_t) noexcept ++ { return __i._M_coro.done(); } + + friend class generator; + @@ libstdc++-v3/include/std/generator (new) + +#if _GLIBCXX_HOSTED + namespace pmr { -+ template<class R, class V = void> -+ using generator = std::generator<R, V, polymorphic_allocator<std::byte>>; ++ template<typename _Ref, typename _Val = void> ++ using generator = std::generator<_Ref, _Val, polymorphic_allocator<std::byte>>; + } +#endif // HOSTED + Tested libstdc++ on x86_64-pc-linux-gnu. TIA, have a lovely day. Arsen Arsenović (2): libstdc++: add missing include in ranges_util.h libstdc++: implement std::generator libstdc++-v3/include/Makefile.am | 2 + libstdc++-v3/include/Makefile.in | 2 + libstdc++-v3/include/bits/elements_of.h | 72 ++ libstdc++-v3/include/bits/ranges_util.h | 1 + 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 + 15 files changed, 1465 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 -- 2.43.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 1/2] libstdc++: add missing include in ranges_util.h 2023-12-21 20:01 [PATCH v2 0/2] libstdc++: generators v2 Arsen Arsenović @ 2023-12-21 20:01 ` Arsen Arsenović 2023-12-21 21:37 ` Jonathan Wakely 2023-12-21 20:01 ` [PATCH v2 2/2] libstdc++: implement std::generator Arsen Arsenović 1 sibling, 1 reply; 10+ messages in thread From: Arsen Arsenović @ 2023-12-21 20:01 UTC (permalink / raw) To: gcc-patches; +Cc: libstdc++, Arsen Arsenović libstdc++-v3/ChangeLog: * include/bits/ranges_util.h: Add missing <bits/invoke.h> include. --- libstdc++-v3/include/bits/ranges_util.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h index 185e46ec7a94..ad61a19dd33d 100644 --- a/libstdc++-v3/include/bits/ranges_util.h +++ b/libstdc++-v3/include/bits/ranges_util.h @@ -33,6 +33,7 @@ #if __cplusplus > 201703L # include <bits/ranges_base.h> # include <bits/utility.h> +# include <bits/invoke.h> #ifdef __glibcxx_ranges namespace std _GLIBCXX_VISIBILITY(default) -- 2.43.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 1/2] libstdc++: add missing include in ranges_util.h 2023-12-21 20:01 ` [PATCH v2 1/2] libstdc++: add missing include in ranges_util.h Arsen Arsenović @ 2023-12-21 21:37 ` Jonathan Wakely 0 siblings, 0 replies; 10+ messages in thread From: Jonathan Wakely @ 2023-12-21 21:37 UTC (permalink / raw) To: Arsen Arsenović; +Cc: gcc-patches, libstdc++ On Thu, 21 Dec 2023 at 21:26, Arsen Arsenović wrote: > > libstdc++-v3/ChangeLog: > > * include/bits/ranges_util.h: Add missing <bits/invoke.h> > include. OK > --- > libstdc++-v3/include/bits/ranges_util.h | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h > index 185e46ec7a94..ad61a19dd33d 100644 > --- a/libstdc++-v3/include/bits/ranges_util.h > +++ b/libstdc++-v3/include/bits/ranges_util.h > @@ -33,6 +33,7 @@ > #if __cplusplus > 201703L > # include <bits/ranges_base.h> > # include <bits/utility.h> > +# include <bits/invoke.h> > > #ifdef __glibcxx_ranges > namespace std _GLIBCXX_VISIBILITY(default) > -- > 2.43.0 > ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v2 2/2] libstdc++: implement std::generator 2023-12-21 20:01 [PATCH v2 0/2] libstdc++: generators v2 Arsen Arsenović 2023-12-21 20:01 ` [PATCH v2 1/2] libstdc++: add missing include in ranges_util.h Arsen Arsenović @ 2023-12-21 20:01 ` Arsen Arsenović 2023-12-21 21:51 ` Jonathan Wakely ` (2 more replies) 1 sibling, 3 replies; 10+ messages in thread From: Arsen Arsenović @ 2023-12-21 20:01 UTC (permalink / raw) To: gcc-patches; +Cc: libstdc++, Arsen Arsenović 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. --- 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/Makefile.am index 368b92eafbc7..ca76afbcc77f 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 \ @@ -123,6 +124,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 a31588c01002..4fa4a259fef3 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 \ @@ -478,6 +479,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 0134a71b3ab4..71cb75dd07a7 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1743,6 +1743,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 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_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..e438fae4b981 --- /dev/null +++ b/libstdc++-v3/include/std/generator @@ -0,0 +1,812 @@ +// <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> + +#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 _Val> + using _Reference_t = __conditional_t<is_void_v<_Val>, + _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 _Val> + using _Yield2_t = _Yield_t<_Reference_t<_Ref, _Val>>; + + template<typename> constexpr bool __is_generator = false; + template<typename _Val, typename _Ref, typename _Alloc> + constexpr bool __is_generator<std::generator<_Val, _Ref, _Alloc>> = true; + + /// 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 class 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 _Tp> + concept _Cv_unqualified_object = is_object_v<_Tp> + && same_as<_Tp, remove_cv_t<_Tp>>; + } // 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<typename _Ref, typename _Val = void> + using generator = std::generator<_Ref, _Val, 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 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 <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.43.0 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] libstdc++: implement std::generator 2023-12-21 20:01 ` [PATCH v2 2/2] libstdc++: implement std::generator Arsen Arsenović @ 2023-12-21 21:51 ` Jonathan Wakely 2023-12-26 20:28 ` Will Hawkins 2024-01-04 10:30 ` Jonathan Wakely 2 siblings, 0 replies; 10+ messages in thread From: Jonathan Wakely @ 2023-12-21 21:51 UTC (permalink / raw) To: Arsen Arsenović; +Cc: gcc-patches, libstdc++ On Thu, 21 Dec 2023 at 21:26, Arsen Arsenović 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. 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/Makefile.am > index 368b92eafbc7..ca76afbcc77f 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 \ > @@ -123,6 +124,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 a31588c01002..4fa4a259fef3 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 \ > @@ -478,6 +479,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 0134a71b3ab4..71cb75dd07a7 100644 > --- a/libstdc++-v3/include/bits/version.def > +++ b/libstdc++-v3/include/bits/version.def > @@ -1743,6 +1743,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 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_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..e438fae4b981 > --- /dev/null > +++ b/libstdc++-v3/include/std/generator > @@ -0,0 +1,812 @@ > +// <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> > + > +#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 _Val> > + using _Reference_t = __conditional_t<is_void_v<_Val>, > + _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 _Val> > + using _Yield2_t = _Yield_t<_Reference_t<_Ref, _Val>>; > + > + template<typename> constexpr bool __is_generator = false; > + template<typename _Val, typename _Ref, typename _Alloc> > + constexpr bool __is_generator<std::generator<_Val, _Ref, _Alloc>> = true; > + > + /// 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 class 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 _Tp> > + concept _Cv_unqualified_object = is_object_v<_Tp> > + && same_as<_Tp, remove_cv_t<_Tp>>; > + } // 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<typename _Ref, typename _Val = void> > + using generator = std::generator<_Ref, _Val, 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 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 <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.43.0 > ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] libstdc++: implement std::generator 2023-12-21 20:01 ` [PATCH v2 2/2] libstdc++: implement std::generator Arsen Arsenović 2023-12-21 21:51 ` Jonathan Wakely @ 2023-12-26 20:28 ` Will Hawkins 2023-12-26 23:04 ` Arsen Arsenović 2024-01-04 10:30 ` Jonathan Wakely 2 siblings, 1 reply; 10+ messages in thread From: Will Hawkins @ 2023-12-26 20:28 UTC (permalink / raw) To: Arsen Arsenović; +Cc: gcc-patches, libstdc++ On Thu, Dec 21, 2023 at 4:26 PM Arsen Arsenović <arsen@aarsen.me> wrote: > > libstdc++-v3/ChangeLog: > ... snip ... > + 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 Should this read: // We're bottom. We're also top if top is unset (note that this is ? Very impressive work -- I learned a ton by reading your implementation. Will ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] libstdc++: implement std::generator 2023-12-26 20:28 ` Will Hawkins @ 2023-12-26 23:04 ` Arsen Arsenović 0 siblings, 0 replies; 10+ messages in thread From: Arsen Arsenović @ 2023-12-26 23:04 UTC (permalink / raw) To: Will Hawkins; +Cc: gcc-patches, libstdc++ [-- Attachment #1: Type: text/plain, Size: 876 bytes --] Hi Will, Will Hawkins <hawkinsw@obs.cr> writes: > On Thu, Dec 21, 2023 at 4:26 PM Arsen Arsenović <arsen@aarsen.me> wrote: >> >> libstdc++-v3/ChangeLog: >> > > ... snip ... > >> + 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 > > Should this read: > > // We're bottom. We're also top if top is unset (note that this is > > ? Yes, I am decently sure so - well spotted. I'll leave your message unread to correct this typo later. Thanks! > Very impressive work -- I learned a ton by reading your implementation. > Will Happy to hear that :-) Have a lovely night! -- Arsen Arsenović [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 381 bytes --] ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] libstdc++: implement std::generator 2023-12-21 20:01 ` [PATCH v2 2/2] libstdc++: implement std::generator Arsen Arsenović 2023-12-21 21:51 ` Jonathan Wakely 2023-12-26 20:28 ` Will Hawkins @ 2024-01-04 10:30 ` Jonathan Wakely 2024-01-04 12:00 ` Arsen Arsenović 2 siblings, 1 reply; 10+ messages in thread From: Jonathan Wakely @ 2024-01-04 10:30 UTC (permalink / raw) To: Arsen Arsenović; +Cc: gcc-patches, libstdc++ On Thu, 21 Dec 2023 at 21:26, Arsen Arsenović wrote: > + > + template<typename _A> > + concept _Stateless_alloc = (allocator_traits<_A>::is_always_equal::value > + && default_initializable<_A>); The _A here is a BADNAME and needs to be more than one letter. FAIL: 17_intro/badnames.cc -std=gnu++23 (test for excess errors) Excess errors: /home/test/src/gcc/libstdc++-v3/testsuite/17_intro/badnames.cc:61: error: expected nested-name-specifier before '_A' ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] libstdc++: implement std::generator 2024-01-04 10:30 ` Jonathan Wakely @ 2024-01-04 12:00 ` Arsen Arsenović 2024-01-04 12:57 ` Jonathan Wakely 0 siblings, 1 reply; 10+ messages in thread From: Arsen Arsenović @ 2024-01-04 12:00 UTC (permalink / raw) To: Jonathan Wakely; +Cc: gcc-patches, libstdc++ [-- Attachment #1: Type: text/plain, Size: 687 bytes --] Jonathan Wakely <jwakely@redhat.com> writes: > On Thu, 21 Dec 2023 at 21:26, Arsen Arsenović wrote: >> + >> + template<typename _A> >> + concept _Stateless_alloc = (allocator_traits<_A>::is_always_equal::value >> + && default_initializable<_A>); > > The _A here is a BADNAME and needs to be more than one letter. > > FAIL: 17_intro/badnames.cc -std=gnu++23 (test for excess errors) > Excess errors: > /home/test/src/gcc/libstdc++-v3/testsuite/17_intro/badnames.cc:61: > error: expected nested-name-specifier before '_A' Ah - my bad, will fix, and push along with that typo fix that Will Hawkins mentioned. -- Arsen Arsenović [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 251 bytes --] ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v2 2/2] libstdc++: implement std::generator 2024-01-04 12:00 ` Arsen Arsenović @ 2024-01-04 12:57 ` Jonathan Wakely 0 siblings, 0 replies; 10+ messages in thread From: Jonathan Wakely @ 2024-01-04 12:57 UTC (permalink / raw) To: Arsen Arsenović; +Cc: gcc-patches, libstdc++ On Thu, 4 Jan 2024 at 12:01, Arsen Arsenović <arsen@aarsen.me> wrote: > > > Jonathan Wakely <jwakely@redhat.com> writes: > > > On Thu, 21 Dec 2023 at 21:26, Arsen Arsenović wrote: > >> + > >> + template<typename _A> > >> + concept _Stateless_alloc = (allocator_traits<_A>::is_always_equal::value > >> + && default_initializable<_A>); > > > > The _A here is a BADNAME and needs to be more than one letter. > > > > FAIL: 17_intro/badnames.cc -std=gnu++23 (test for excess errors) > > Excess errors: > > /home/test/src/gcc/libstdc++-v3/testsuite/17_intro/badnames.cc:61: > > error: expected nested-name-specifier before '_A' > > Ah - my bad, will fix, and push along with that typo fix that Will > Hawkins mentioned. Thanks (and thanks, Will). ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2024-01-04 12:57 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2023-12-21 20:01 [PATCH v2 0/2] libstdc++: generators v2 Arsen Arsenović 2023-12-21 20:01 ` [PATCH v2 1/2] libstdc++: add missing include in ranges_util.h Arsen Arsenović 2023-12-21 21:37 ` Jonathan Wakely 2023-12-21 20:01 ` [PATCH v2 2/2] libstdc++: implement std::generator Arsen Arsenović 2023-12-21 21:51 ` Jonathan Wakely 2023-12-26 20:28 ` Will Hawkins 2023-12-26 23:04 ` Arsen Arsenović 2024-01-04 10:30 ` Jonathan Wakely 2024-01-04 12:00 ` Arsen Arsenović 2024-01-04 12:57 ` Jonathan Wakely
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).