From patchwork Sat Nov 18 18:46:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: =?utf-8?q?Arsen_Arsenovi=C4=87?= X-Patchwork-Id: 166649 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a59:9910:0:b0:403:3b70:6f57 with SMTP id i16csp1364501vqn; Sat, 18 Nov 2023 12:54:14 -0800 (PST) X-Google-Smtp-Source: AGHT+IEqo9NjVNe6oKie9F0dNSrMpqs74yBFjiebew4TLnCSzBXAqO/m9PgSDPo4PK7vjsQIFQ41 X-Received: by 2002:a05:620a:46a6:b0:777:7240:8de with SMTP id bq38-20020a05620a46a600b00777724008demr3660310qkb.8.1700340854616; Sat, 18 Nov 2023 12:54:14 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1700340854; cv=pass; d=google.com; s=arc-20160816; b=IUM8+0vv5tPET6ZC8zivFqCPOo5b6N4XIgZUrtdJpkd6ENxsMBSJVMjcTF6IEmDeZl kk2kkJcJRDoMBUhRxX0n90XLnJZtudfZ5hFJ1rsdcPJFsj2IWlD+LfT/b4k7fQ2JWyCU x81Ga+rpIcYtQW0rJH/fEKBiZwhUUfuobi4q92GrRIV5olVNXGHuaNTVOW1Lu0/rj1dk gDhF4SnfiCIZy6Ewp1rlLmpcigCUFvRbnzikDLoz6H1wJMQDjn4J1pP4TZKK87YqcyRt CpJDsXylSv/VbXV0NQdaLkGtp53lSyrzZ1qr9aSVvh+Z2WHy7xaQXIDjNs06w5wc3ysv iRMA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:message-id:date:subject:cc:to:from:dkim-signature :arc-filter:dmarc-filter:delivered-to; bh=ciByF0y7l2dQsdo0t4s2BpiT6RxkqyS9cAfnxv3oikM=; fh=pZArBBE5gqcFSMViUIMBC37HN/RJW9xr4RQd+RzEHRs=; b=Z3qlEJ4EiRVn8WJ3kNfPOvPNORSh1/vXMhTl9OnfG3pnIpvUAwgmGGeT8ibZxhOhPK 9x8yH/6KAf5oMh4p9RDibaLlTtrG6+mm921d14rFkGqu1XaX++stZniN2WWz4y5BdTpG R4aPgyeN3RVtWs5cQMRHkOhWbmhow4G9Cqqe/za+AAQyhZ9vnu1Sh6rvKS+vI8meq3vg UkPaFfVlaNs3aLp+UFSaUyDpdKKghxU9QeLOj1r2Dg9sW9qgBdvS1zyf9wXOd40nGSRu WFdFpWU34dkWmDTegXTBd7KVAfRjZaFKvp+3mvLdOplu65i9m1hHU/12sX62HRfFSz2c Jfvw== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@aarsen.me header.s=MBO0001 header.b=RUuagHM3; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=aarsen.me Received: from server2.sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id bs10-20020a05620a470a00b0077772d4821esi4481659qkb.291.2023.11.18.12.54.14 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 18 Nov 2023 12:54:14 -0800 (PST) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@aarsen.me header.s=MBO0001 header.b=RUuagHM3; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=aarsen.me Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 41F243858012 for ; Sat, 18 Nov 2023 19:50:50 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mout-p-101.mailbox.org (mout-p-101.mailbox.org [IPv6:2001:67c:2050:0:465::101]) by sourceware.org (Postfix) with ESMTPS id D13C03858D20; Sat, 18 Nov 2023 19:50:17 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org D13C03858D20 Authentication-Results: sourceware.org; dmarc=pass (p=reject dis=none) header.from=aarsen.me Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=aarsen.me ARC-Filter: OpenARC Filter v1.0.0 sourceware.org D13C03858D20 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2001:67c:2050:0:465::101 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700337023; cv=none; b=DNG0MK80PVoXwCRQot9gGfhGOqwDkb7FfN7EEv0dmjKWYtPZxLaEpkxFFgXAW/f0FlmRVrX8uZNRQCpmWLPRSIcg6aXH5C/mhsejdt7Te6GSlWeaXqVLGUIhedW+YigvsA5lfWSX9VXOsh3C0zwAJtsBQJiimgyyoVytHQW5H1U= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1700337023; c=relaxed/simple; bh=SDmhorMcDekKxq317FYNZSmil5cbQlw0rxFfoc7uX54=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=v/eVz+45e//OoYzmmpUNsfzDYdIsqDDq+0GV85u8YhDU6g7lz+r6FIAI88dHjBMZ2nFtjiHehINXhq4fxv4R8hWeHHziJX0qjDS5rYvYh5SJUL6XP3T7kb129lryI9vvtj/0MFWso3VvGHmhpZI43PFUyw6Bsr3thM8z4CE6hlY= ARC-Authentication-Results: i=1; server2.sourceware.org Received: from smtp102.mailbox.org (smtp102.mailbox.org [10.196.197.102]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-101.mailbox.org (Postfix) with ESMTPS id 4SXkrk5BP7z9sq4; Sat, 18 Nov 2023 20:50:14 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=aarsen.me; s=MBO0001; t=1700337014; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding; bh=ciByF0y7l2dQsdo0t4s2BpiT6RxkqyS9cAfnxv3oikM=; b=RUuagHM3QD41nHpbKPAaAU0R83thH1ngssHDZducNXfqO2d4V04YSaWDHUxqyTvJgFaXwN K1KM0YWmr7TCm6hEnpAZY/MFLYyZjE2iuImbTeGc3oAlRTGBx3BPIjpHlTvv353yPYJIwO Qg0Xc/k3sWJsLZvfQTA0o5Di5dqZQJYss82HzqE8+AW48ZvSPQgbPGfbDrSdyo18zueKQu rYEafuo/FiBh/CKYJjG+5C8ayA0j7awDXHgVzABQWuponV9kBTb755RHgoMooVYPHCWzIE 3FpuAHCgV+y8txceqTaPkLCWa6XwbZDg2eFRrb+0EWNrrS3TYUP9GtnnD1rNqw== From: =?utf-8?q?Arsen_Arsenovi=C4=87?= To: gcc-patches@gcc.gnu.org Cc: libstdc++@gcc.gnu.org, =?utf-8?q?Arsen_Arsenovi=C4=87?= Subject: [PATCH] libstdc++: implement std::generator Date: Sat, 18 Nov 2023 19:46:56 +0100 Message-ID: <20231118195008.579211-1-arsen@aarsen.me> MIME-Version: 1.0 X-Spam-Status: No, score=-11.0 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_INFOUSMEBIZ, KAM_SHORT, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1782936611924205137 X-GMAIL-MSGID: 1782936611924205137 libstdc++-v3/ChangeLog: * include/Makefile.am: Install std/generator, bits/elements_of.h as freestanding. * include/Makefile.in: Regenerate. * include/bits/version.def: Add __cpp_lib_generator. * include/bits/version.h: Regenerate. * include/precompiled/stdc++.h: Include . * include/std/ranges: Include bits/elements_of.h * include/bits/elements_of.h: New file. * include/std/generator: New file. * testsuite/24_iterators/range_generators/01.cc: New test. * testsuite/24_iterators/range_generators/02.cc: New test. * testsuite/24_iterators/range_generators/copy.cc: New test. * testsuite/24_iterators/range_generators/except.cc: New test. * testsuite/24_iterators/range_generators/synopsis.cc: New test. * testsuite/24_iterators/range_generators/subrange.cc: New test. --- Evening, This is an implementation of from C++23. It should be feature-complete, though it doesn't have all the tests that it ought to and is missing a few tweaks. Posting to get reviews in the meanwhile, in case something obvious was missed. Have a lovely night :-) libstdc++-v3/include/Makefile.am | 2 + libstdc++-v3/include/Makefile.in | 2 + libstdc++-v3/include/bits/elements_of.h | 72 ++ libstdc++-v3/include/bits/version.def | 9 + libstdc++-v3/include/bits/version.h | 11 + libstdc++-v3/include/precompiled/stdc++.h | 1 + libstdc++-v3/include/std/generator | 820 ++++++++++++++++++ libstdc++-v3/include/std/ranges | 4 + .../24_iterators/range_generators/01.cc | 55 ++ .../24_iterators/range_generators/02.cc | 219 +++++ .../24_iterators/range_generators/copy.cc | 97 +++ .../24_iterators/range_generators/except.cc | 97 +++ .../24_iterators/range_generators/subrange.cc | 45 + .../24_iterators/range_generators/synopsis.cc | 38 + 14 files changed, 1472 insertions(+) create mode 100644 libstdc++-v3/include/bits/elements_of.h create mode 100644 libstdc++-v3/include/std/generator create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/01.cc create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/02.cc create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/except.cc create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/subrange.cc create mode 100644 libstdc++-v3/testsuite/24_iterators/range_generators/synopsis.cc diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am index 17d9d9cec313..0b764f2b8a9e 100644 --- a/libstdc++-v3/include/Makefile.am +++ b/libstdc++-v3/include/Makefile.am @@ -35,6 +35,7 @@ std_freestanding = \ ${std_srcdir}/coroutine \ ${std_srcdir}/expected \ ${std_srcdir}/functional \ + ${std_srcdir}/generator \ ${std_srcdir}/iterator \ ${std_srcdir}/limits \ ${std_srcdir}/memory \ @@ -122,6 +123,7 @@ bits_freestanding = \ ${bits_srcdir}/concept_check.h \ ${bits_srcdir}/char_traits.h \ ${bits_srcdir}/cpp_type_traits.h \ + ${bits_srcdir}/elements_of.h \ ${bits_srcdir}/enable_special_members.h \ ${bits_srcdir}/functexcept.h \ ${bits_srcdir}/functional_hash.h \ diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in index f038af709cc4..7f1a6592942e 100644 --- a/libstdc++-v3/include/Makefile.in +++ b/libstdc++-v3/include/Makefile.in @@ -393,6 +393,7 @@ std_freestanding = \ ${std_srcdir}/coroutine \ ${std_srcdir}/expected \ ${std_srcdir}/functional \ + ${std_srcdir}/generator \ ${std_srcdir}/iterator \ ${std_srcdir}/limits \ ${std_srcdir}/memory \ @@ -477,6 +478,7 @@ bits_freestanding = \ ${bits_srcdir}/concept_check.h \ ${bits_srcdir}/char_traits.h \ ${bits_srcdir}/cpp_type_traits.h \ + ${bits_srcdir}/elements_of.h \ ${bits_srcdir}/enable_special_members.h \ ${bits_srcdir}/functexcept.h \ ${bits_srcdir}/functional_hash.h \ diff --git a/libstdc++-v3/include/bits/elements_of.h b/libstdc++-v3/include/bits/elements_of.h new file mode 100644 index 000000000000..663e15a94aa7 --- /dev/null +++ b/libstdc++-v3/include/bits/elements_of.h @@ -0,0 +1,72 @@ +// Tag type for yielding ranges rather than values in -*- C++ -*- + +// Copyright (C) 2023 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +#ifndef _GLIBCXX_BITS_ELEMENTS_OF +#define _GLIBCXX_BITS_ELEMENTS_OF + +#pragma GCC system_header + +#include + +#include + +// C++ >= 23 && __glibcxx_coroutine +#if defined(__glibcxx_ranges) && defined(__glibcxx_generator) +#include +#include + +#if _GLIBCXX_HOSTED +# include // likely desirable if hosted. +#endif // HOSTED + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION +namespace ranges +{ + + /** + * @ingroup ranges + * @since C++23 + * @{ + */ + + template> + struct elements_of + { + [[no_unique_address]] _Range range; + [[no_unique_address]] _Alloc allocator = _Alloc(); + }; + + template> + elements_of(_Range&&, _Alloc = _Alloc()) + -> elements_of<_Range&&, _Alloc>; + + /// @} +} +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std + +#endif // __glibcxx_generator && __glibcxx_ranges +#endif // _GLIBCXX_BITS_ELEMENTS_OF diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 447fdeb95193..1edee0dbd27b 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1649,6 +1649,15 @@ ftms = { }; }; +ftms = { + name = generator; + values = { + v = 202207; + cxxmin = 23; + extra_cond = "__glibcxx_coroutine"; + }; +}; + // Standard test specifications. stds[97] = ">= 199711L"; stds[03] = ">= 199711L"; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 97c6d8508f48..b323ea9dab9e 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2021,4 +2021,15 @@ #endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) */ #undef __glibcxx_want_to_string +// from version.def line 1637 +#if !defined(__cpp_lib_generator) +# if (__cplusplus >= 202302L) && (__glibcxx_coroutine) +# define __glibcxx_generator 202207L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_generator) +# define __cpp_lib_generator 202207L +# endif +# endif +#endif /* !defined(__cpp_lib_generator) && defined(__glibcxx_want_generator) */ +#undef __glibcxx_want_generator + #undef __glibcxx_want_all diff --git a/libstdc++-v3/include/precompiled/stdc++.h b/libstdc++-v3/include/precompiled/stdc++.h index 176ad79ff3c3..baad95dd6c3e 100644 --- a/libstdc++-v3/include/precompiled/stdc++.h +++ b/libstdc++-v3/include/precompiled/stdc++.h @@ -222,6 +222,7 @@ #if __cplusplus > 202002L #include +#include #include #if __has_include() # include diff --git a/libstdc++-v3/include/std/generator b/libstdc++-v3/include/std/generator new file mode 100644 index 000000000000..a06811d3dd96 --- /dev/null +++ b/libstdc++-v3/include/std/generator @@ -0,0 +1,820 @@ +// -*- C++ -*- + +// Copyright (C) 2023 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +/** @file include/generator + * This is a Standard C++ Library header. + */ + +#ifndef _GLIBCXX_GENERATOR +#define _GLIBCXX_GENERATOR + +#include +#pragma GCC system_header + +#include + +#define __glibcxx_want_generator +#include + +#if __cplusplus < 202302L +# error "std::generator is a C++23 extension" +#endif + +#ifdef __cpp_lib_generator // C++ >= 23 && __glibcxx_coroutine +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#if _GLIBCXX_HOSTED +# include +#endif // HOSTED + +namespace std _GLIBCXX_VISIBILITY(default) +{ +_GLIBCXX_BEGIN_NAMESPACE_VERSION + + /** + * @defgroup generator_coros Range generator coroutines + * @addtogroup ranges + * @since C++23 + * @{ + */ + + /** @brief A range specified using a yielding coroutine. + * + * `std::generator` is a utility class for defining ranges using coroutines + * that yield elements as a range. Generator coroutines are synchronous. + * + * @headerfile generator + * @since C++23 + */ + template + class generator; + + /// @cond undocumented + namespace __gen + { + /// _Reference type for a generator whose reference (first argument) and + /// value (second argument) types are _Ref and _V. + template + using _Reference_t = __conditional_t, + _Ref&&, _Ref>; + + /// Type yielded by a generator whose _Reference type is _Reference. + template + using _Yield_t = __conditional_t, + _Reference, + const _Reference&>; + + /// _Yield_t * _Reference_t + template + using _Yield2_t = _Yield_t<_Reference_t<_Ref, _V>>; + + template struct _Is_generator_t : std::false_type {}; + template + struct _Is_generator_t<::std::generator<_V, _R, _A>> : std::true_type {}; + + template + concept _Is_generator = _Is_generator_t>::value; + + + /// Allocator and value type erased generator promise type. + /// \tparam _Yielded The corresponding generators yielded type. + template + class _Promise_erased + { + static_assert(is_reference_v<_Yielded>); + using _Yielded_deref = 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 + friend struct std::generator; + + template + struct _Recursive_awaiter; + template + friend struct _Recursive_awaiter; + struct _Copy_awaiter; + struct _Subyield_state; + struct _Final_awaiter; + public: + suspend_always + initial_suspend() const noexcept + { return {}; } + + suspend_always + yield_value(_Yielded __val) noexcept + { + _M_bottom_value() = ::std::addressof(__val); + return {}; + } + + auto + yield_value(const _Yielded_deref& __val) + noexcept (is_nothrow_constructible_v<_Yielded_decvref, + const _Yielded_deref&>) + requires (is_rvalue_reference_v<_Yielded> + && constructible_from<_Yielded_decvref, + const _Yielded_deref&>) + { return _Copy_awaiter(__val, _M_bottom_value()); } + + template + requires std::same_as<_Yield2_t<_R2, _V2>, _Yielded> + auto + yield_value(ranges::elements_of&&, _U2> __r) + noexcept + { return _Recursive_awaiter { std::move(__r.range) }; } + + template + requires convertible_to, _Yielded> + auto + yield_value(ranges::elements_of<_R, _Alloc> __r) + noexcept + { + auto __n = [] (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 + 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 + struct _Promise_erased<_Yielded>::_Final_awaiter + { + bool await_ready() noexcept + { return false; } + + template + auto await_suspend(std::coroutine_handle<_Promise> __c) noexcept + { + static_assert(is_pointer_interconvertible_base_of_v< + _Promise_erased, _Promise>); + + auto& __n = __c.promise()._M_nest; + return __n._M_pop(); + } + + void await_resume() noexcept {} + }; + + template + struct _Promise_erased<_Yielded>::_Copy_awaiter + { + _Yielded_decvref _M_value; + _ValuePtr& _M_bottom_value; + + constexpr bool await_ready() noexcept + { return false; } + + template + void await_suspend(std::coroutine_handle<_Promise>) noexcept + { + static_assert(is_pointer_interconvertible_base_of_v< + _Promise_erased, _Promise>); + _M_bottom_value = ::std::addressof(_M_value); + } + + constexpr void + await_resume() const noexcept + {} + }; + + template + template + struct _Promise_erased<_Yielded>::_Recursive_awaiter + { + _Gen _M_gen; + static_assert(_Is_generator<_Gen>); + static_assert(std::same_as); + + _Recursive_awaiter(_Gen __gen) noexcept + : _M_gen(std::move(__gen)) + { this->_M_gen._M_mark_as_started(); } + + constexpr bool + await_ready() const noexcept + { return false; } + + + template + std::coroutine_handle<> + await_suspend(std::coroutine_handle<_Promise> __p) noexcept + { + static_assert(is_pointer_interconvertible_base_of_v< + _Promise_erased, _Promise>); + + auto __c = _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 + concept _Stateless_alloc = (allocator_traits<_A>::is_always_equal::value + && default_initializable<_A>); + + template + 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, + "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(__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 + void* + operator new(std::size_t __sz, + allocator_arg_t, const _NA& __na, + const _Args&...) + requires convertible_to + { + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)), + __sz); + } + + template + void* + operator new(std::size_t __sz, + const _This&, + allocator_arg_t, const _NA& __na, + const _Args&...) + requires convertible_to + { + return _M_allocate(static_cast<_Rebound>(static_cast<_Alloc>(__na)), + __sz); + } + + void + operator delete(void* __ptr, std::size_t __csz) noexcept + { + if constexpr (_Stateless_alloc<_Rebound>) + { + _Rebound __b; + return __b.deallocate(reinterpret_cast<_Alloc_block*>(__ptr), + _Alloc_block::_M_cnt(__csz)); + } + else + { + auto __nsz = _Alloc_block::_M_cnt(_M_alloc_size(__csz)); + auto __fn = reinterpret_cast(__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 + { + 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 + 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(__da); + __aan += sizeof(_Dealloc_fn); + auto __aligned = ((__aan + __ba - 1) / __ba) * __ba; + return reinterpret_cast<_Rebound*>(__aligned); + } + + template + static auto + _M_alloc_size(std::size_t __csz) noexcept + { + // This time, we want the coroutine frame, then the deallocator + // pointer, then the allocator itself, if any. + std::size_t __aa = 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 + 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(__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 + 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, + "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(__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(__sz); + _Dealloc_fn __d = [] (void* __ptr, std::size_t __sz) + { + ::operator delete(__ptr, _M_alloc_size(__sz)); + }; + auto __p = ::operator new(__nsz); + auto __pn = reinterpret_cast(__p); + *_M_dealloc_address(__pn, __sz) = __d; + return __p; + } + + template + void* + operator new(std::size_t __sz, + allocator_arg_t, const _NA& __na, + const _Args&...) + { return _M_allocate(__na, __sz); } + + template + void* + operator new(std::size_t __sz, + const _This&, + allocator_arg_t, const _NA& __na, + const _Args&...) + { return _M_allocate(__na, __sz); } + + void + operator delete(void* __ptr, std::size_t __sz) noexcept + { + _Dealloc_fn __d; + auto __pn = reinterpret_cast(__ptr); + __d = *_M_dealloc_address(__pn, __sz); + __d(__ptr, __sz); + } + }; + + template + concept _Cv_unqualified_object = is_object_v<_T> + && same_as<_T, remove_cv_t<_T>>; + } // namespace __gen + /// @endcond + + template + class generator + : public ranges::view_interface> + { + using _Value = __conditional_t, 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::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 __coro) noexcept + : _M_coro { move(__coro) } + {} + + void + _M_mark_as_started() noexcept + { + __glibcxx_assert(!this->_M_began); + this->_M_began = true; + } + + coroutine_handle _M_coro; + bool _M_began = false; + }; + + template + 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(*__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 + using generator = std::generator>; + } +#endif // HOSTED + +_GLIBCXX_END_NAMESPACE_VERSION +} // namespace std +#endif // __cpp_lib_generator + +#endif // _GLIBCXX_GENERATOR diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 26d6c013ad04..fde8c6cd706d 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -67,6 +67,10 @@ #define __glibcxx_want_ranges_zip #include +#ifdef __glibcxx_generator // C++ >= 23 && __glibcxx_coroutine +# include +#endif + /** * @defgroup ranges Ranges * diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc new file mode 100644 index 000000000000..bedbec3d353f --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/01.cc @@ -0,0 +1,55 @@ +// { dg-do run { target c++23 } } +// Copyright (C) 2023 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +#include +#include + +// basic example +std::generator +bar() +{ + co_yield 3; + co_yield 4; +} + +std::generator +foo() +{ + co_yield 1; + co_yield 2; + co_yield std::ranges::elements_of { bar() }; + co_yield 5; +} + +int +main() +{ + for (auto x : foo()) + std::cout << x << '\n'; +} + +// { dg-output {1(\n|\r\n|\r)} } +// { dg-output {2(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {4(\n|\r\n|\r)} } +// { dg-output {5(\n|\r\n|\r)} } diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc new file mode 100644 index 000000000000..570daedca75b --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/02.cc @@ -0,0 +1,219 @@ +// { dg-do run { target c++23 } } +// Copyright (C) 2023 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +#include +#include +#include + +struct foo +{ + int id; + + foo(int id) + : id { id } + {} + + foo(const foo& o) + : id { o.id * 100 } + { + std::cout << "copy-consed " << o.id << "->" << id << '\n'; + } + + foo& + operator=(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 +foogen() +{ + co_yield foo{0}; + + { + foo f {1}; + co_yield f; + } + + { + const foo f {2}; + co_yield f; + } + + { + foo f {3}; + co_yield std::move(f); + } +} + +std::generator +foogen2() +{ + co_yield foo{0}; + + { + foo f {1}; + co_yield f; + } + + { + const foo f {2}; + co_yield f; + } + + { + foo f {3}; + co_yield std::move(f); + } +} + +std::generator +foogen3() +{ + co_yield foo{0}; + + { + foo f {1}; + co_yield f; + } + + { + const foo f {2}; + co_yield f; + } + + { + foo f {3}; + co_yield std::move(f); + } +} + +int +main() +{ + for (auto f : foogen()) + std::cout << f.id << '\n'; + for (const auto& f : foogen()) + std::cout << f.id << '\n'; + for (auto&& f : foogen()) + std::cout << f.id << '\n'; + + std::cout << "---\n"; + + for (auto f : foogen2()) + std::cout << f.id << '\n'; + for (const auto& f : foogen2()) + std::cout << f.id << '\n'; + for (auto&& f : foogen2()) + std::cout << f.id << '\n'; + + std::cout << "---\n"; + + for (auto f : foogen3()) + std::cout << f.id << '\n'; + for (const auto& f : foogen3()) + std::cout << f.id << '\n'; + for (auto&& f : foogen3()) + std::cout << f.id << '\n'; +} + +// { dg-output {moved 0(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } +// { dg-output {moved 100(\n|\r\n|\r)} } +// { dg-output {100(\n|\r\n|\r)} } +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } +// { dg-output {moved 200(\n|\r\n|\r)} } +// { dg-output {200(\n|\r\n|\r)} } +// { dg-output {moved 3(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } +// { dg-output {100(\n|\r\n|\r)} } +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } +// { dg-output {200(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } +// { dg-output {100(\n|\r\n|\r)} } +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } +// { dg-output {200(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {---(\n|\r\n|\r)} } +// { dg-output {copy-consed 0->0(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } +// { dg-output {100(\n|\r\n|\r)} } +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } +// { dg-output {200(\n|\r\n|\r)} } +// { dg-output {copy-consed 3->300(\n|\r\n|\r)} } +// { dg-output {300(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {1(\n|\r\n|\r)} } +// { dg-output {2(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {1(\n|\r\n|\r)} } +// { dg-output {2(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {---(\n|\r\n|\r)} } +// { dg-output {moved 0(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } +// { dg-output {moved 100(\n|\r\n|\r)} } +// { dg-output {100(\n|\r\n|\r)} } +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } +// { dg-output {moved 200(\n|\r\n|\r)} } +// { dg-output {200(\n|\r\n|\r)} } +// { dg-output {moved 3(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } +// { dg-output {100(\n|\r\n|\r)} } +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } +// { dg-output {200(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } +// { dg-output {0(\n|\r\n|\r)} } +// { dg-output {copy-consed 1->100(\n|\r\n|\r)} } +// { dg-output {100(\n|\r\n|\r)} } +// { dg-output {copy-consed 2->200(\n|\r\n|\r)} } +// { dg-output {200(\n|\r\n|\r)} } +// { dg-output {3(\n|\r\n|\r)} } diff --git a/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc b/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc new file mode 100644 index 000000000000..5e5474d0de53 --- /dev/null +++ b/libstdc++-v3/testsuite/24_iterators/range_generators/copy.cc @@ -0,0 +1,97 @@ +// { dg-do run { target c++23 } } +// Copyright (C) 2023 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// Under Section 7 of GPL version 3, you are granted additional +// permissions described in the GCC Runtime Library Exception, version +// 3.1, as published by the Free Software Foundation. + +// You should have received a copy of the GNU General Public License and +// a copy of the GCC Runtime Library Exception along with this program; +// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see +// . + +#include +#include + +template +struct copy_max +{ + int copy = 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 +std::generator +foo() +{ + co_yield {}; +} + +int +main() +{ + static_assert(!std::copy_constructible>); + { + auto gen = foo&>(); + auto i = gen.begin(); + *i; + *i; + auto is = *i; + VERIFY(is.copy > 0); + } + + { + auto gen2 = foo&&>(); + auto i = gen2.begin(); + *i; + *i; + auto is = *i; + } + + { + auto gen = foo>(); // 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 +// . + +#include +#include + +std::generator +foo() +{ + co_yield 0; + throw 3; /* dice roll */ +} + +std::generator +foo_delegator() +{ + co_yield 1; + co_yield std::ranges::elements_of { foo() }; +} + +bool catchy_caught = false; + +std::generator +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 +// . + +#include +#include +#include +#include + +std::generator +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 +// . + +#include + +#if !defined(__cpp_lib_generator) || __cpp_lib_generator < 202207L +# error "__cpp_lib_generator undefined or has wrong value" +#endif + +namespace test { + using std::generator; +#if __STDC_HOSTED__ + namespace pmr { + using std::pmr::generator; + } +#endif +}