From patchwork Thu Jan 11 22:16:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 187508 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7300:2411:b0:101:2151:f287 with SMTP id m17csp1784457dyi; Thu, 11 Jan 2024 15:23:50 -0800 (PST) X-Google-Smtp-Source: AGHT+IH2tGKrQ3HKGfam/g/IumTCpynucV+j3nBlx7rVTJ3VlLkf7fGee8qmqD3RbJlhTL2ptTK/ X-Received: by 2002:a05:6808:17a1:b0:3bb:cfcc:39f2 with SMTP id bg33-20020a05680817a100b003bbcfcc39f2mr83945oib.89.1705015430317; Thu, 11 Jan 2024 15:23:50 -0800 (PST) ARC-Seal: i=2; a=rsa-sha256; t=1705015430; cv=pass; d=google.com; s=arc-20160816; b=Nq+nys+yF027qMpHkbGWgo3uUANT+NWOJBu1Xl9akSkGmmA2U2wRn89JsnXCEWGkW7 dhwCXjCepb67GyMLeNzQEU8fJ1o3icV3U5EHQZrFc7qLBxvn+LuCZSqOPwa21J/ERuqE I/rkYNLuqO3FoUONHH8HTELMzK5OrWJm+1G68melnHi2VplRqlTvK4vghjOWefrD6jh1 /QtyQk6NyYr0QtbEv0M0jnDEainMSnCTWbjr0PiJ/p99V63MM8ALun30u2uuYO9+qMlI zwg7++LsyZkt2VB5cixAFxh7KfeNb/83eUlMXMSxfqakffI07tmD58ZEa3b9omtkajsW Uq+w== 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=C3heBPVPsyEoXUSzvREv0aE7v8quy21HJM7DRoqLBgs=; fh=xxr0vWKOO9BgJNk07XBFBEc3dQfxUagOkdh7jkhoLZo=; b=d5N2rbu+BCa9ZJdkXqUlGMrK3xIa5HUN4XwfNiWRT8TwwsV5eZFXtjwy4dy1+vwTAd KP2YpYpadEGPsEOXbwqJzIQ5COQhponPg0chS+HNkPapvvhYWQsAs9/CggOlPkFODlHc k+Nl0GSentQUHcBNMdbmds/Xw4B0eWQt3Za2bBR2K/vji+GNqpQo8acZSelGxsraQOQZ iG73YuE77JcK66K4aGwxUVdEzQAAb0kHk1ekndQqaqQS0v9PdjmJKZJqkmpxCJhWle2k rSNhhoaPlbg0O24rnSUv8Z/a/x/0sGFkGKZsgNIbDq1KSKIw/sbUTReae2PLh2ZeKjW+ V7rA== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@redhat.com header.s=mimecast20190719 header.b=bUfE5L3X; 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=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (server2.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id g6-20020a0ce4c6000000b00680d46d3a88si1761749qvm.587.2024.01.11.15.23.50 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 11 Jan 2024 15:23:50 -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=@redhat.com header.s=mimecast20190719 header.b=bUfE5L3X; 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=NONE sp=NONE dis=NONE) header.from=redhat.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 68977385E83F for ; Thu, 11 Jan 2024 22:17:57 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTPS id 4B0093858D1E for ; Thu, 11 Jan 2024 22:16:55 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 4B0093858D1E Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=redhat.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=redhat.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 4B0093858D1E Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=170.10.133.124 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1705011421; cv=none; b=RFuL4PEmO5k6wj17AEPorASDJ+TbJs8ex3I8Kchu3eXga4paJklIMdYZlVR1adL9/1Zr6kthIv3ja9NMNg0o85RdoF2NLRvmyZzP2ssTYbM78l0rpbApVXbMz3xCcKlyOPQG3PBlQOULxHDnoh4t97hCUAn0pdEZU6kilxmG+1g= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1705011421; c=relaxed/simple; bh=MtYzwQcfCiy7oZsTDmLzhlvfsU7QlIPCGntNdXUIwSA=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=Eglt1pVkT8eAqv2S3Asi4CviprefuGOhoJTrwhI/14fH8N2S5KypkOIFHtqicTENdN4u6rDE8HZw5BhQWqCB6tW2VdMY4kqMeCLZNa2ZAahmtZoofz3UYIFW3N3h8FJbDAdelqyXvJJiYdQ3jY/2DNaxFE/7Hasvw+6mwfKt6Bg= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1705011414; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=C3heBPVPsyEoXUSzvREv0aE7v8quy21HJM7DRoqLBgs=; b=bUfE5L3X834Hnd05tuDc0t7BDkN2dOSqUU1T25UXgZkopnht6gXxHx8/ZTuiUjn3JpRxVy YUpPaC5Y6hM3VyU+cjqx5L462CX29QPotPfvdonvNcfpx7U62cpdXRO/d1V0BxSmfKV06J fNRVnOs2ykAkrRmowtCVYRe+dt9HLQU= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-620-KJJ3ieHWMoSdS1iDwkPkdw-1; Thu, 11 Jan 2024 17:16:53 -0500 X-MC-Unique: KJJ3ieHWMoSdS1iDwkPkdw-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.rdu2.redhat.com [10.11.54.1]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 1579285A589; Thu, 11 Jan 2024 22:16:53 +0000 (UTC) Received: from localhost (unknown [10.42.28.185]) by smtp.corp.redhat.com (Postfix) with ESMTP id 877F83C25; Thu, 11 Jan 2024 22:16:52 +0000 (UTC) From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Cc: Patrick Palka , Ville Voutilainen Subject: [PATCH] libstdc++: Implement P2255R2 dangling checks for std::tuple [PR108822] Date: Thu, 11 Jan 2024 22:16:35 +0000 Message-ID: <20240111221651.585639-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-13.1 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, RCVD_IN_MSPIKE_H3, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org 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: 1787838260140956375 X-GMAIL-MSGID: 1787838260140956375 I'd like to commit this to trunk for GCC 14. Please take a look. -- >8 -- This is the last part of PR libstdc++/108822 implementing P2255R2, which makes it ill-formed to create a std::tuple that would bind a reference to a temporary. The dangling checks are implemented as deleted constructors for C++20 and higher, and as Debug Mode static assertions in the constructor body for older standards. This is similar to the r13-6084-g916ce577ad109b changes for std::pair. As part of this change, I've reimplemented most of std::tuple for C++20, making use of concepts to replace the enable_if constraints, and using conditional explicit to avoid duplicating most constructors. We could use conditional explicit for the C++11 implementation too (with pragmas to disables the -Wc++17-extensions warnings), but that should be done as a stage 1 change for GCC 15 rather than now. The partial specialization for std::tuple is no longer used for C++20 (or more precisely, for a C++20 compiler that supports concepts and conditional explicit). The additional constructors and assignment operators that take std::pair arguments have been added to the C++20 implementation of the primary template, with sizeof...(_Elements)==2 constraints. This avoids reimplementing all the other constructors in the std::tuple partial specialization to use concepts. This way we avoid four implementations of every constructor and only have three! (The primary template has an implementation of each constructor for C++11 and another for C++20, and the tuple specialization has an implementation of each for C++11, so that's three for each constructor.) In order to make the constraints more efficient on the C++20 version of the default constructor I've also added a variable template for the __is_implicitly_default_constructible trait, implemented using concepts. libstdc++-v3/ChangeLog: PR libstdc++/108822 * include/std/tuple (tuple): Add checks for dangling references. Reimplement constraints and constant expressions using C++20 features. * include/std/type_traits [C++20] (__is_implicitly_default_constructible_v): Define. (__is_implicitly_default_constructible): Use variable template. * testsuite/20_util/tuple/dangling_ref.cc: New test. --- libstdc++-v3/include/std/tuple | 1021 ++++++++++++----- libstdc++-v3/include/std/type_traits | 11 + .../testsuite/20_util/tuple/dangling_ref.cc | 105 ++ 3 files changed, 841 insertions(+), 296 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/tuple/dangling_ref.cc diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 50e11843757..cd05b638923 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -752,11 +752,467 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template class tuple : public _Tuple_impl<0, _Elements...> { - typedef _Tuple_impl<0, _Elements...> _Inherited; + using _Inherited = _Tuple_impl<0, _Elements...>; template using _TCC = _TupleConstraints<_Cond, _Elements...>; +#if __cpp_concepts && __cpp_conditional_explicit // >= C++20 + template + static consteval bool + __constructible() + { + if constexpr (sizeof...(_UTypes) == sizeof...(_Elements)) + return (is_constructible_v<_Elements, _UTypes> && ...); + else + return false; + } + + template + static consteval bool + __nothrow_constructible() + { + if constexpr (sizeof...(_UTypes) == sizeof...(_Elements)) + return (is_nothrow_constructible_v<_Elements, _UTypes> && ...); + else + return false; + } + + template + static consteval bool + __convertible() + { + if constexpr (sizeof...(_UTypes) == sizeof...(_Elements)) + return (is_convertible_v<_UTypes, _Elements> && ...); + else + return false; + } + + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 3121. tuple constructor constraints for UTypes&&... overloads + template + static consteval bool + __disambiguating_constraint() + { + if constexpr (sizeof...(_Elements) != sizeof...(_UTypes)) + return false; + else if constexpr (sizeof...(_Elements) == 1) + { + using _U0 = typename _Nth_type<0, _UTypes...>::type; + return !is_same_v, tuple>; + } + else if constexpr (sizeof...(_Elements) < 4) + { + using _U0 = typename _Nth_type<0, _UTypes...>::type; + if constexpr (!is_same_v, allocator_arg_t>) + return true; + else + { + using _T0 = typename _Nth_type<0, _Elements...>::type; + return is_same_v, allocator_arg_t>; + } + } + return true; + } + + // Return true iff sizeof...(Types) == 1 && tuple_size_v == 1 + // and the single element in Types can be initialized from TUPLE, + // or is the same type as tuple_element_t<0, TUPLE>. + template + static consteval bool + __use_other_ctor() + { + if constexpr (sizeof...(_Elements) != 1) + return false; + else if constexpr (is_same_v, tuple>) + return true; // Should use a copy/move constructor instead. + else + { + using _Tp = typename _Nth_type<0, _Elements...>::type; + if constexpr (is_convertible_v<_Tuple, _Tp>) + return true; + else if constexpr (is_constructible_v<_Tp, _Tuple>) + return true; + } + return false; + } + + template + static consteval bool + __dangles() + { +#if __has_builtin(__reference_constructs_from_temporary) + return (__reference_constructs_from_temporary(_Elements, _Up&&) + || ...); +#else + return false; +#endif + } + + public: + constexpr + explicit(!(__is_implicitly_default_constructible_v<_Elements> && ...)) + tuple() + noexcept((is_nothrow_default_constructible_v<_Elements> && ...)) + requires (is_default_constructible_v<_Elements> && ...) + : _Inherited() + { } + + constexpr explicit(!__convertible()) + tuple(const _Elements&... __elements) + noexcept(__nothrow_constructible()) + requires (__constructible()) + : _Inherited(__elements...) + { } + + template + requires (__disambiguating_constraint<_UTypes...>()) + && (__constructible<_UTypes...>()) + && (!__dangles<_UTypes...>()) + constexpr explicit(!__convertible<_UTypes...>()) + tuple(_UTypes&&... __u) + noexcept(__nothrow_constructible<_UTypes...>()) + : _Inherited(std::forward<_UTypes>(__u)...) + { } + + template + requires (__disambiguating_constraint<_UTypes...>()) + && (__constructible<_UTypes...>()) + && (__dangles<_UTypes...>()) + tuple(_UTypes&&...) = delete; + + constexpr tuple(const tuple&) = default; + + constexpr tuple(tuple&&) = default; + + template + requires (__constructible()) + && (!__use_other_ctor&>()) + && (!__dangles()) + constexpr explicit(!__convertible()) + tuple(const tuple<_UTypes...>& __u) + noexcept(__nothrow_constructible()) + : _Inherited(static_cast&>(__u)) + { } + + template + requires (__constructible()) + && (!__use_other_ctor&>()) + && (__dangles()) + tuple(const tuple<_UTypes...>&) = delete; + + template + requires (__constructible<_UTypes...>()) + && (!__use_other_ctor>()) + && (!__dangles<_UTypes...>()) + constexpr explicit(!__convertible<_UTypes...>()) + tuple(tuple<_UTypes...>&& __u) + noexcept(__nothrow_constructible<_UTypes...>()) + : _Inherited(static_cast<_Tuple_impl<0, _UTypes...>&&>(__u)) + { } + + template + requires (__constructible<_UTypes...>()) + && (!__use_other_ctor>()) + && (__dangles<_UTypes...>()) + tuple(tuple<_UTypes...>&&) = delete; + +#if __cpp_lib_ranges_zip // >= C++23 + template + requires (__constructible<_UTypes&...>()) + && (!__use_other_ctor&>()) + && (!__dangles<_UTypes&...>()) + constexpr explicit(!__convertible<_UTypes&...>()) + tuple(tuple<_UTypes...>& __u) + noexcept(__nothrow_constructible<_UTypes&...>()) + : _Inherited(static_cast<_Tuple_impl<0, _UTypes...>&>(__u)) + { } + + template + requires (__constructible<_UTypes&...>()) + && (!__use_other_ctor&>()) + && (__dangles<_UTypes&...>()) + tuple(tuple<_UTypes...>&) = delete; + + template + requires (__constructible()) + && (!__use_other_ctor>()) + && (!__dangles()) + constexpr explicit(!__convertible()) + tuple(const tuple<_UTypes...>&& __u) + noexcept(__nothrow_constructible()) + : _Inherited(static_cast&&>(__u)) + { } + + template + requires (__constructible()) + && (!__use_other_ctor>()) + && (__dangles()) + tuple(const tuple<_UTypes...>&&) = delete; +#endif // C++23 + + template + requires (sizeof...(_Elements) == 2) + && (__constructible()) + && (!__dangles()) + constexpr explicit(!__convertible()) + tuple(const pair<_U1, _U2>& __u) + noexcept(__nothrow_constructible()) + : _Inherited(__u.first, __u.second) + { } + + template + requires (sizeof...(_Elements) == 2) + && (__constructible()) + && (__dangles()) + tuple(const pair<_U1, _U2>&) = delete; + + template + requires (sizeof...(_Elements) == 2) + && (__constructible<_U1, _U2>()) + && (!__dangles<_U1, _U2>()) + constexpr explicit(!__convertible<_U1, _U2>()) + tuple(pair<_U1, _U2>&& __u) + noexcept(__nothrow_constructible<_U1, _U2>()) + : _Inherited(std::forward<_U1>(__u.first), + std::forward<_U2>(__u.second)) + { } + + template + requires (sizeof...(_Elements) == 2) + && (__constructible<_U1, _U2>()) + && (__dangles<_U1, _U2>()) + tuple(pair<_U1, _U2>&&) = delete; + +#if __cpp_lib_ranges_zip // >= C++23 + template + requires (sizeof...(_Elements) == 2) + && (__constructible<_U1&, _U2&>()) + && (!__dangles<_U1&, _U2&>()) + constexpr explicit(!__convertible<_U1&, _U2&>()) + tuple(pair<_U1, _U2>& __u) + noexcept(__nothrow_constructible<_U1&, _U2&>()) + : _Inherited(__u.first, __u.second) + { } + + template + requires (sizeof...(_Elements) == 2) + && (__constructible<_U1&, _U2&>()) + && (__dangles<_U1&, _U2&>()) + tuple(pair<_U1, _U2>&) = delete; + + template + requires (sizeof...(_Elements) == 2) + && (__constructible()) + && (!__dangles()) + constexpr explicit(!__convertible()) + tuple(const pair<_U1, _U2>&& __u) + noexcept(__nothrow_constructible()) + : _Inherited(std::forward(__u.first), + std::forward(__u.second)) + { } + + template + requires (sizeof...(_Elements) == 2) + && (__constructible()) + && (__dangles()) + tuple(const pair<_U1, _U2>&&) = delete; +#endif // C++23 + +#if 0 && __cpp_lib_tuple_like // >= C++23 + template<__tuple_like _UTuple> + constexpr explicit(...) + tuple(_UTuple&& __u); +#endif // C++23 + + // Allocator-extended constructors. + + template + constexpr + explicit(!(__is_implicitly_default_constructible_v<_Elements> && ...)) + tuple(allocator_arg_t __tag, const _Alloc& __a) + requires (is_default_constructible_v<_Elements> && ...) + : _Inherited(__tag, __a) + { } + + template + constexpr explicit(!__convertible()) + tuple(allocator_arg_t __tag, const _Alloc& __a, + const _Elements&... __elements) + requires (__constructible()) + : _Inherited(__tag, __a, __elements...) + { } + + template + requires (__disambiguating_constraint<_UTypes...>()) + && (__constructible<_UTypes...>()) + && (!__dangles<_UTypes...>()) + constexpr explicit(!__convertible<_UTypes...>()) + tuple(allocator_arg_t __tag, const _Alloc& __a, _UTypes&&... __u) + : _Inherited(__tag, __a, std::forward<_UTypes>(__u)...) + { } + + template + requires (__disambiguating_constraint<_UTypes...>()) + && (__constructible<_UTypes...>()) + && (__dangles<_UTypes...>()) + tuple(allocator_arg_t, const _Alloc&, _UTypes&&...) = delete; + + template + constexpr + tuple(allocator_arg_t __tag, const _Alloc& __a, const tuple& __u) + : _Inherited(__tag, __a, static_cast(__u)) + { } + + template + requires (__constructible<_Elements...>()) + constexpr + tuple(allocator_arg_t __tag, const _Alloc& __a, tuple&& __u) + : _Inherited(__tag, __a, static_cast<_Inherited&&>(__u)) + { } + + template + requires (__constructible()) + && (!__use_other_ctor&>()) + && (!__dangles()) + constexpr explicit(!__convertible()) + tuple(allocator_arg_t __tag, const _Alloc& __a, + const tuple<_UTypes...>& __u) + : _Inherited(__tag, __a, + static_cast&>(__u)) + { } + + template + requires (__constructible()) + && (!__use_other_ctor&>()) + && (__dangles()) + tuple(allocator_arg_t, const _Alloc&, const tuple<_UTypes...>&) = delete; + + template + requires (__constructible<_UTypes...>()) + && (!__use_other_ctor>()) + && (!__dangles<_UTypes...>()) + constexpr explicit(!__use_other_ctor>()) + tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_UTypes...>&& __u) + : _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _UTypes...>&&>(__u)) + { } + + template + requires (__constructible<_UTypes...>()) + && (!__use_other_ctor>()) + && (__dangles<_UTypes...>()) + tuple(allocator_arg_t, const _Alloc&, tuple<_UTypes...>&&) = delete; + +#if __cpp_lib_ranges_zip // >= C++23 + template + requires (__constructible<_UTypes&...>()) + && (!__use_other_ctor&>()) + && (!__dangles<_UTypes&...>()) + constexpr explicit(!__convertible<_UTypes&...>()) + tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_UTypes...>& __u) + : _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _UTypes...>&>(__u)) + { } + + template + requires (__constructible<_UTypes&...>()) + && (!__use_other_ctor&>()) + && (__dangles<_UTypes&...>()) + tuple(allocator_arg_t, const _Alloc&, tuple<_UTypes...>&) = delete; + + template + requires (__constructible()) + && (!__use_other_ctor>()) + && (!__dangles()) + constexpr explicit(!__convertible()) + tuple(allocator_arg_t __tag, const _Alloc& __a, + const tuple<_UTypes...>&& __u) + : _Inherited(__tag, __a, + static_cast&&>(__u)) + { } + + template + requires (__constructible()) + && (!__use_other_ctor>()) + && (__dangles()) + tuple(allocator_arg_t, const _Alloc&, const tuple<_UTypes...>&&) = delete; +#endif // C++23 + + template + requires (sizeof...(_Elements) == 2) + && (__constructible()) + && (!__dangles()) + constexpr explicit(!__convertible()) + tuple(allocator_arg_t __tag, const _Alloc& __a, + const pair<_U1, _U2>& __u) + noexcept(__nothrow_constructible()) + : _Inherited(__tag, __a, __u.first, __u.second) + { } + + template + requires (sizeof...(_Elements) == 2) + && (__constructible()) + && (__dangles()) + tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&) = delete; + + template + requires (sizeof...(_Elements) == 2) + && (__constructible<_U1, _U2>()) + && (!__dangles<_U1, _U2>()) + constexpr explicit(!__convertible<_U1, _U2>()) + tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __u) + noexcept(__nothrow_constructible<_U1, _U2>()) + : _Inherited(__tag, __a, std::move(__u.first), std::move(__u.second)) + { } + + template + requires (sizeof...(_Elements) == 2) + && (__constructible<_U1, _U2>()) + && (__dangles<_U1, _U2>()) + tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&&) = delete; + +#if __cpp_lib_ranges_zip // >= C++23 + template + requires (sizeof...(_Elements) == 2) + && (__constructible<_U1&, _U2&>()) + && (!__dangles<_U1&, _U2&>()) + constexpr explicit(!__convertible<_U1&, _U2&>()) + tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>& __u) + noexcept(__nothrow_constructible<_U1&, _U2&>()) + : _Inherited(__tag, __a, __u.first, __u.second) + { } + + template + requires (sizeof...(_Elements) == 2) + && (__constructible<_U1&, _U2&>()) + && (__dangles<_U1&, _U2&>()) + tuple(allocator_arg_t, const _Alloc&, pair<_U1, _U2>&) = delete; + + template + requires (sizeof...(_Elements) == 2) + && (__constructible()) + && (!__dangles()) + constexpr explicit(!__convertible()) + tuple(allocator_arg_t __tag, const _Alloc& __a, + const pair<_U1, _U2>&& __u) + noexcept(__nothrow_constructible()) + : _Inherited(__tag, __a, std::move(__u.first), std::move(__u.second)) + { } + + template + requires (sizeof...(_Elements) == 2) + && (__constructible()) + && (__dangles()) + tuple(allocator_arg_t, const _Alloc&, const pair<_U1, _U2>&&) = delete; +#endif // C++23 + +#if 0 && __cpp_lib_tuple_like // >= C++23 + template + constexpr explicit(...) + tuple(allocator_arg_t __tag, const _Alloc& __a, _UTuple&& __u); +#endif // C++23 + +#else // !(concepts && conditional_explicit) + // Constraint for non-explicit default constructor template using _ImplicitDefaultCtor = __enable_if_t< @@ -850,15 +1306,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static constexpr bool __use_other_ctor() { return _UseOtherCtor<_Tuple>::value; } -#if __cplusplus > 202002L - template - static constexpr bool __constructible - = _TCC::template __constructible<_Args...>::value; - - template - static constexpr bool __convertible - = _TCC::template __convertible<_Args...>::value; -#endif // C++23 + /// @cond undocumented +#undef __glibcxx_no_dangling_refs +#if __has_builtin(__reference_constructs_from_temporary) \ + && defined _GLIBCXX_DEBUG + // Error if construction from U... would create a dangling ref. +# if __cpp_fold_expressions +# define __glibcxx_dangling_refs(U) \ + (__reference_constructs_from_temporary(_Elements, U) && ...) +# else +# define __glibcxx_dangling_refs(U) \ + __or_<__bool_constant<__reference_constructs_from_temporary(_Elements, U) \ + >...>::value +# endif +# define __glibcxx_no_dangling_refs(U) \ + static_assert(!__glibcxx_dangling_refs(U), \ + "std::tuple constructor creates a dangling reference") +#else +# define __glibcxx_no_dangling_refs(U) +#endif + /// @endcond public: template()) - : _Inherited(std::forward<_UElements>(__elements)...) { } + : _Inherited(std::forward<_UElements>(__elements)...) + { __glibcxx_no_dangling_refs(_UElements&&); } template(), @@ -903,7 +1371,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION explicit constexpr tuple(_UElements&&... __elements) noexcept(__nothrow_constructible<_UElements...>()) - : _Inherited(std::forward<_UElements>(__elements)...) { } + : _Inherited(std::forward<_UElements>(__elements)...) + { __glibcxx_no_dangling_refs(_UElements&&); } constexpr tuple(const tuple&) = default; @@ -917,7 +1386,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(const tuple<_UElements...>& __in) noexcept(__nothrow_constructible()) : _Inherited(static_cast&>(__in)) - { } + { __glibcxx_no_dangling_refs(const _UElements&); } template& __in) noexcept(__nothrow_constructible()) : _Inherited(static_cast&>(__in)) - { } + { __glibcxx_no_dangling_refs(const _UElements&); } template&& __in) noexcept(__nothrow_constructible<_UElements...>()) - : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { } + : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) + { __glibcxx_no_dangling_refs(_UElements&&); } template&& __in) noexcept(__nothrow_constructible<_UElements...>()) - : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { } - -#if __cplusplus > 202002L - template - requires (sizeof...(_Elements) == sizeof...(_UElements)) - && (!__use_other_ctor&>()) - && __constructible<_UElements&...> - explicit(!__convertible<_UElements&...>) - constexpr - tuple(tuple<_UElements...>& __in) - noexcept(__nothrow_constructible<_UElements&...>()) - : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&>(__in)) - { } - - template - requires (sizeof...(_Elements) == sizeof...(_UElements)) - && (!__use_other_ctor&&>()) - && __constructible - explicit(!__convertible) - constexpr - tuple(const tuple<_UElements...>&& __in) - noexcept(__nothrow_constructible()) - : _Inherited(static_cast&&>(__in)) { } -#endif // C++23 + : _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) + { __glibcxx_no_dangling_refs(_UElements&&); } // Allocator-extended constructors. @@ -1000,7 +1448,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(allocator_arg_t __tag, const _Alloc& __a, _UElements&&... __elements) : _Inherited(__tag, __a, std::forward<_UElements>(__elements)...) - { } + { __glibcxx_no_dangling_refs(_UElements&&); } template(), @@ -1010,7 +1458,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(allocator_arg_t __tag, const _Alloc& __a, _UElements&&... __elements) : _Inherited(__tag, __a, std::forward<_UElements>(__elements)...) - { } + { __glibcxx_no_dangling_refs(_UElements&&); } template _GLIBCXX20_CONSTEXPR @@ -1030,8 +1478,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(allocator_arg_t __tag, const _Alloc& __a, const tuple<_UElements...>& __in) : _Inherited(__tag, __a, - static_cast&>(__in)) - { } + static_cast&>(__in)) + { __glibcxx_no_dangling_refs(const _UElements&); } template& __in) : _Inherited(__tag, __a, - static_cast&>(__in)) - { } + static_cast&>(__in)) + { __glibcxx_no_dangling_refs(const _UElements&); } template&& __in) : _Inherited(__tag, __a, - static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) - { } + static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) + { __glibcxx_no_dangling_refs(_UElements&&); } template&& __in) : _Inherited(__tag, __a, - static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) - { } - -#if __cplusplus > 202002L - template - requires (sizeof...(_Elements) == sizeof...(_UElements)) - && (!__use_other_ctor&>()) - && __constructible<_UElements&...> - explicit(!__convertible<_UElements&...>) - constexpr - tuple(allocator_arg_t __tag, const _Alloc& __a, - tuple<_UElements...>& __in) - : _Inherited(__tag, __a, - static_cast<_Tuple_impl<0, _UElements...>&>(__in)) - { } - - template - requires (sizeof...(_Elements) == sizeof...(_UElements)) - && (!__use_other_ctor>()) - && __constructible - explicit(!__convertible) - constexpr - tuple(allocator_arg_t __tag, const _Alloc& __a, - const tuple<_UElements...>&& __in) - : _Inherited(__tag, __a, - static_cast&&>(__in)) - { } -#endif // C++23 + static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) + { __glibcxx_no_dangling_refs(_UElements&&); } +#endif // concepts && conditional_explicit // tuple assignment +#if __cpp_concepts // >= C++20 + private: + template + static consteval bool + __assignable() + { + if constexpr (sizeof...(_UTypes) == sizeof...(_Elements)) + return (is_assignable_v<_Elements&, _UTypes> && ...); + else + return false; + } + + template + static consteval bool + __nothrow_assignable() + { + if constexpr (sizeof...(_UTypes) == sizeof...(_Elements)) + return (is_nothrow_assignable_v<_Elements&, _UTypes> && ...); + else + return false; + } + +#if __cpp_lib_ranges_zip // >= C++23 + template + static consteval bool + __const_assignable() + { + if constexpr (sizeof...(_UTypes) == sizeof...(_Elements)) + return (is_assignable_v && ...); + else + return false; + } +#endif // C++23 + + public: + + tuple& operator=(const tuple& __u) = delete; + + constexpr tuple& + operator=(const tuple& __u) + noexcept(__nothrow_assignable()) + requires (__assignable()) + { + this->_M_assign(__u); + return *this; + } + + constexpr tuple& + operator=(tuple&& __u) + noexcept(__nothrow_assignable<_Elements...>()) + requires (__assignable<_Elements...>()) + { + this->_M_assign(std::move(__u)); + return *this; + } + + template + requires (__assignable()) + constexpr tuple& + operator=(const tuple<_UTypes...>& __u) + noexcept(__nothrow_assignable()) + { + this->_M_assign(__u); + return *this; + } + + template + requires (__assignable<_UTypes...>()) + constexpr tuple& + operator=(tuple<_UTypes...>&& __u) + noexcept(__nothrow_assignable<_UTypes...>()) + { + this->_M_assign(std::move(__u)); + return *this; + } + +#if __cpp_lib_ranges_zip // >= C++23 + constexpr const tuple& + operator=(const tuple& __u) const + requires (__const_assignable()) + { + this->_M_assign(__u); + return *this; + } + + constexpr const tuple& + operator=(tuple&& __u) const + requires (__const_assignable<_Elements...>()) + { + this->_M_assign(std::move(__u)); + return *this; + } + + template + constexpr const tuple& + operator=(const tuple<_UTypes...>& __u) const + requires (__const_assignable()) + { + this->_M_assign(__u); + return *this; + } + + template + constexpr const tuple& + operator=(tuple<_UTypes...>&& __u) const + requires (__const_assignable<_UTypes...>()) + { + this->_M_assign(std::move(__u)); + return *this; + } +#endif // C++23 + + template + requires (__assignable()) + constexpr tuple& + operator=(const pair<_U1, _U2>& __u) + noexcept(__nothrow_assignable()) + { + this->_M_head(*this) = __u.first; + this->_M_tail(*this)._M_head(*this) = __u.second; + return *this; + } + + template + requires (__assignable<_U1, _U2>()) + constexpr tuple& + operator=(pair<_U1, _U2>&& __u) + noexcept(__nothrow_assignable<_U1, _U2>()) + { + this->_M_head(*this) = std::forward<_U1>(__u.first); + this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__u.second); + return *this; + } + +#if __cpp_lib_ranges_zip // >= C++23 + template + requires (__const_assignable()) + constexpr const tuple& + operator=(const pair<_U1, _U2>& __u) const + { + this->_M_head(*this) = __u.first; + this->_M_tail(*this)._M_head(*this) = __u.second; + return *this; + } + + template + requires (__const_assignable<_U1, _U2>()) + constexpr const tuple& + operator=(pair<_U1, _U2>&& __u) const + { + this->_M_head(*this) = std::forward<_U1>(__u.first); + this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__u.second); + return *this; + } +#endif // C++23 + +#if 0 && __cpp_lib_tuple_like // >= C++23 + template<__tuple_like _UTuple> + constexpr tuple& + operator=(_UTuple&& __u); + + template<__tuple_like _UTuple> + constexpr tuple& + operator=(_UTuple&& __u) const; +#endif // C++23 + +#else // concepts + _GLIBCXX20_CONSTEXPR tuple& operator=(__conditional_t<__assignable(), @@ -1137,44 +1728,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION this->_M_assign(std::move(__in)); return *this; } - -#if __cplusplus > 202002L - constexpr const tuple& - operator=(const tuple& __in) const - requires (is_copy_assignable_v && ...) - { - this->_M_assign(__in); - return *this; - } - - constexpr const tuple& - operator=(tuple&& __in) const - requires (is_assignable_v && ...) - { - this->_M_assign(std::move(__in)); - return *this; - } - - template - constexpr const tuple& - operator=(const tuple<_UElements...>& __in) const - requires (sizeof...(_Elements) == sizeof...(_UElements)) - && (is_assignable_v && ...) - { - this->_M_assign(__in); - return *this; - } - - template - constexpr const tuple& - operator=(tuple<_UElements...>&& __in) const - requires (sizeof...(_Elements) == sizeof...(_UElements)) - && (is_assignable_v && ...) - { - this->_M_assign(std::move(__in)); - return *this; - } -#endif // C++23 +#endif // concepts // tuple swap _GLIBCXX20_CONSTEXPR @@ -1183,7 +1737,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(__and_<__is_nothrow_swappable<_Elements>...>::value) { _Inherited::_M_swap(__in); } -#if __cplusplus > 202002L +#if __cpp_lib_ranges_zip // >= C++23 // As an extension, we constrain the const swap member function in order // to continue accepting explicit instantiation of tuples whose elements // are not all const swappable. Without this constraint, such an @@ -1233,6 +1787,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(allocator_arg_t, const _Alloc&, const tuple&) noexcept { } }; +#if !(__cpp_concepts && __cpp_conditional_explicit) // >= C++20 /// Partial specialization, 2-element tuple. /// Includes construction and assignment from a pair. template @@ -1300,15 +1855,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static constexpr bool __is_alloc_arg() { return is_same<__remove_cvref_t<_U1>, allocator_arg_t>::value; } -#if __cplusplus > 202002L - template - static constexpr bool __constructible - = _TCC::template __constructible<_U1, _U2>::value; - - template - static constexpr bool __convertible - = _TCC::template __convertible<_U1, _U2>::value; -#endif // C++23 + /// @cond undocumented +#undef __glibcxx_no_dangling_refs + // Error if construction from _U1 and _U2 would create a dangling ref. +#if __has_builtin(__reference_constructs_from_temporary) \ + && defined _GLIBCXX_DEBUG +# define __glibcxx_no_dangling_refs(_U1, _U2) \ + static_assert(!__reference_constructs_from_temporary(_T1, _U1) \ + && !__reference_constructs_from_temporary(_T2, _U2), \ + "std::tuple constructor creates a dangling reference") +#else +# define __glibcxx_no_dangling_refs(_U1, _U2) +#endif + /// @endcond public: template()) - : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { } + : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template(), _U1, _U2> = false> explicit constexpr tuple(_U1&& __a1, _U2&& __a2) noexcept(__nothrow_constructible<_U1, _U2>()) - : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) { } + : _Inherited(std::forward<_U1>(__a1), std::forward<_U2>(__a2)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } constexpr tuple(const tuple&) = default; @@ -1362,60 +1923,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION constexpr tuple(const tuple<_U1, _U2>& __in) noexcept(__nothrow_constructible()) - : _Inherited(static_cast&>(__in)) { } + : _Inherited(static_cast&>(__in)) + { __glibcxx_no_dangling_refs(const _U1&, const _U2&); } template = false> explicit constexpr tuple(const tuple<_U1, _U2>& __in) noexcept(__nothrow_constructible()) - : _Inherited(static_cast&>(__in)) { } + : _Inherited(static_cast&>(__in)) + { __glibcxx_no_dangling_refs(const _U1&, const _U2&); } template = true> constexpr tuple(tuple<_U1, _U2>&& __in) noexcept(__nothrow_constructible<_U1, _U2>()) - : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { } + : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template = false> explicit constexpr tuple(tuple<_U1, _U2>&& __in) noexcept(__nothrow_constructible<_U1, _U2>()) - : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { } - -#if __cplusplus > 202002L - template - requires __constructible<_U1&, _U2&> - explicit(!__convertible<_U1&, _U2&>) - constexpr - tuple(tuple<_U1, _U2>& __in) - noexcept(__nothrow_constructible<_U1&, _U2&>()) - : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&>(__in)) { } - - template - requires __constructible - explicit(!__convertible) - constexpr - tuple(const tuple<_U1, _U2>&& __in) - noexcept(__nothrow_constructible()) - : _Inherited(static_cast&&>(__in)) { } -#endif // C++23 + : _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template = true> constexpr tuple(const pair<_U1, _U2>& __in) noexcept(__nothrow_constructible()) - : _Inherited(__in.first, __in.second) { } + : _Inherited(__in.first, __in.second) + { __glibcxx_no_dangling_refs(const _U1&, const _U2&); } template = false> explicit constexpr tuple(const pair<_U1, _U2>& __in) noexcept(__nothrow_constructible()) - : _Inherited(__in.first, __in.second) { } + : _Inherited(__in.first, __in.second) + { __glibcxx_no_dangling_refs(const _U1&, const _U2&); } template = true> @@ -1423,7 +1972,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(pair<_U1, _U2>&& __in) noexcept(__nothrow_constructible<_U1, _U2>()) : _Inherited(std::forward<_U1>(__in.first), - std::forward<_U2>(__in.second)) { } + std::forward<_U2>(__in.second)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template = false> @@ -1431,26 +1981,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(pair<_U1, _U2>&& __in) noexcept(__nothrow_constructible<_U1, _U2>()) : _Inherited(std::forward<_U1>(__in.first), - std::forward<_U2>(__in.second)) { } - -#if __cplusplus > 202002L - template - requires __constructible<_U1&, _U2&> - explicit(!__convertible<_U1&, _U2&>) - constexpr - tuple(pair<_U1, _U2>& __in) - noexcept(__nothrow_constructible<_U1&, _U2&>()) - : _Inherited(__in.first, __in.second) { } - - template - requires __constructible - explicit(!__convertible) - constexpr - tuple(const pair<_U1, _U2>&& __in) - noexcept(__nothrow_constructible()) - : _Inherited(std::forward(__in.first), - std::forward(__in.second)) { } -#endif // C++23 + std::forward<_U2>(__in.second)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } // Allocator-extended constructors. @@ -1480,7 +2012,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX20_CONSTEXPR tuple(allocator_arg_t __tag, const _Alloc& __a, _U1&& __a1, _U2&& __a2) : _Inherited(__tag, __a, std::forward<_U1>(__a1), - std::forward<_U2>(__a2)) { } + std::forward<_U2>(__a2)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template = false> @@ -1489,7 +2022,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(allocator_arg_t __tag, const _Alloc& __a, _U1&& __a1, _U2&& __a2) : _Inherited(__tag, __a, std::forward<_U1>(__a1), - std::forward<_U2>(__a2)) { } + std::forward<_U2>(__a2)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template _GLIBCXX20_CONSTEXPR @@ -1507,8 +2041,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(allocator_arg_t __tag, const _Alloc& __a, const tuple<_U1, _U2>& __in) : _Inherited(__tag, __a, - static_cast&>(__in)) - { } + static_cast&>(__in)) + { __glibcxx_no_dangling_refs(const _U1&, const _U2&); } template = false> @@ -1517,15 +2051,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION tuple(allocator_arg_t __tag, const _Alloc& __a, const tuple<_U1, _U2>& __in) : _Inherited(__tag, __a, - static_cast&>(__in)) - { } + static_cast&>(__in)) + { __glibcxx_no_dangling_refs(const _U1&, const _U2&); } template = true> _GLIBCXX20_CONSTEXPR tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_U1, _U2>&& __in) : _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) - { } + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template = false> @@ -1533,36 +2067,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX20_CONSTEXPR tuple(allocator_arg_t __tag, const _Alloc& __a, tuple<_U1, _U2>&& __in) : _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) - { } - -#if __cplusplus > 202002L - template - requires __constructible<_U1&, _U2&> - explicit(!__convertible<_U1&, _U2&>) - constexpr - tuple(allocator_arg_t __tag, const _Alloc& __a, - tuple<_U1, _U2>& __in) - : _Inherited(__tag, __a, - static_cast<_Tuple_impl<0, _U1, _U2>&>(__in)) - { } - - template - requires __constructible - explicit(!__convertible) - constexpr - tuple(allocator_arg_t __tag, const _Alloc& __a, - const tuple<_U1, _U2>&& __in) - : _Inherited(__tag, __a, - static_cast&&>(__in)) - { } -#endif // C++23 + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template = true> _GLIBCXX20_CONSTEXPR tuple(allocator_arg_t __tag, const _Alloc& __a, const pair<_U1, _U2>& __in) - : _Inherited(__tag, __a, __in.first, __in.second) { } + : _Inherited(__tag, __a, __in.first, __in.second) + { __glibcxx_no_dangling_refs(const _U1&, const _U2&); } template = false> @@ -1570,14 +2083,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX20_CONSTEXPR tuple(allocator_arg_t __tag, const _Alloc& __a, const pair<_U1, _U2>& __in) - : _Inherited(__tag, __a, __in.first, __in.second) { } + : _Inherited(__tag, __a, __in.first, __in.second) + { __glibcxx_no_dangling_refs(const _U1&, const _U2&); } template = true> _GLIBCXX20_CONSTEXPR tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __in) : _Inherited(__tag, __a, std::forward<_U1>(__in.first), - std::forward<_U2>(__in.second)) { } + std::forward<_U2>(__in.second)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } template = false> @@ -1585,25 +2100,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX20_CONSTEXPR tuple(allocator_arg_t __tag, const _Alloc& __a, pair<_U1, _U2>&& __in) : _Inherited(__tag, __a, std::forward<_U1>(__in.first), - std::forward<_U2>(__in.second)) { } - -#if __cplusplus > 202002L - template - requires __constructible<_U1&, _U2&> - explicit(!__convertible<_U1&, _U2&>) - constexpr - tuple(allocator_arg_t __tag, const _Alloc& __a, - pair<_U1, _U2>& __in) - : _Inherited(__tag, __a, __in.first, __in.second) { } - - template - requires __constructible - explicit(!__convertible) - constexpr - tuple(allocator_arg_t __tag, const _Alloc& __a, const pair<_U1, _U2>&& __in) - : _Inherited(__tag, __a, std::forward(__in.first), - std::forward(__in.second)) { } -#endif // C++23 + std::forward<_U2>(__in.second)) + { __glibcxx_no_dangling_refs(_U1&&, _U2&&); } // Tuple assignment. @@ -1649,44 +2147,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return *this; } -#if __cplusplus > 202002L - constexpr const tuple& - operator=(const tuple& __in) const - requires is_copy_assignable_v && is_copy_assignable_v - { - this->_M_assign(__in); - return *this; - } - - constexpr const tuple& - operator=(tuple&& __in) const - requires is_assignable_v && is_assignable_v - { - this->_M_assign(std::move(__in)); - return *this; - } - - template - constexpr const tuple& - operator=(const tuple<_U1, _U2>& __in) const - requires is_assignable_v - && is_assignable_v - { - this->_M_assign(__in); - return *this; - } - - template - constexpr const tuple& - operator=(tuple<_U1, _U2>&& __in) const - requires is_assignable_v - && is_assignable_v - { - this->_M_assign(std::move(__in)); - return *this; - } -#endif // C++23 - template _GLIBCXX20_CONSTEXPR __enable_if_t<__assignable(), tuple&> @@ -1709,47 +2169,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return *this; } -#if __cplusplus > 202002L - template - constexpr const tuple& - operator=(const pair<_U1, _U2>& __in) const - requires is_assignable_v - && is_assignable_v - { - this->_M_head(*this) = __in.first; - this->_M_tail(*this)._M_head(*this) = __in.second; - return *this; - } - - template - constexpr const tuple& - operator=(pair<_U1, _U2>&& __in) const - requires is_assignable_v - && is_assignable_v - { - this->_M_head(*this) = std::forward<_U1>(__in.first); - this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__in.second); - return *this; - } -#endif // C++23 - _GLIBCXX20_CONSTEXPR void swap(tuple& __in) noexcept(__and_<__is_nothrow_swappable<_T1>, __is_nothrow_swappable<_T2>>::value) { _Inherited::_M_swap(__in); } - -#if __cplusplus > 202002L - constexpr void - swap(const tuple& __in) const - noexcept(__and_v<__is_nothrow_swappable, - __is_nothrow_swappable>) - requires is_swappable_v && is_swappable_v - { _Inherited::_M_swap(__in); } -#endif // C++23 }; - +#endif // concepts && conditional_explicit /// class tuple_size template @@ -2174,7 +2601,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION noexcept(noexcept(__x.swap(__y))) { __x.swap(__y); } -#if __cplusplus > 202002L +#if __cpp_lib_ranges_zip // >= C++23 template requires (is_swappable_v && ...) constexpr void @@ -2329,7 +2756,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif -#if __cplusplus > 202002L +#if __cpp_lib_ranges_zip // >= C++23 template class _TQual, template class _UQual> requires requires { typename tuple, _UQual<_UTypes>>...>; } @@ -2344,6 +2771,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// @} +#undef __glibcxx_no_dangling_refs + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index b6b680a3c58..a9bb2806ca9 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -1306,6 +1306,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION "template argument must be a complete class or an unbounded array"); }; +#if __cpp_variable_templates && __cpp_concepts + template + constexpr bool __is_implicitly_default_constructible_v + = requires (void(&__f)(_Tp)) { __f({}); }; + + template + struct __is_implicitly_default_constructible + : __bool_constant<__is_implicitly_default_constructible_v<_Tp>> + { }; +#else struct __do_is_implicitly_default_constructible_impl { template @@ -1335,6 +1345,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : public __and_<__is_constructible_impl<_Tp>, __is_implicitly_default_constructible_safe<_Tp>>::type { }; +#endif /// is_trivially_copy_constructible template diff --git a/libstdc++-v3/testsuite/20_util/tuple/dangling_ref.cc b/libstdc++-v3/testsuite/20_util/tuple/dangling_ref.cc new file mode 100644 index 00000000000..c6c8e0c3ef4 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/tuple/dangling_ref.cc @@ -0,0 +1,105 @@ +// { dg-do compile { target c++11 } } +// { dg-options "-Wno-unused-variable" } +// { dg-additional-options "-D_GLIBCXX_DEBUG" { target c++17_down } } + +#include +#include + +#if __cplusplus >= 202002L +// For C++20 and later, constructors are constrained to disallow dangling. +static_assert(!std::is_constructible_v, long, int>); +static_assert(!std::is_constructible_v, int, long>); +static_assert(!std::is_constructible_v, + std::tuple>); +static_assert(!std::is_constructible_v, + std::tuple>); +static_assert(!std::is_constructible_v, + const std::tuple&>); +static_assert(!std::is_constructible_v, + const std::tuple&>); +static_assert(!std::is_constructible_v, + std::pair>); +static_assert(!std::is_constructible_v, + std::pair>); +static_assert(!std::is_constructible_v, + const std::pair&>); +static_assert(!std::is_constructible_v, + const std::pair&>); +#endif + +void +test_ary_ctors() +{ + std::tuple t1(1L, 2); + // { dg-error "here" "" { target { c++17_down && hosted } } 33 } + // { dg-error "use of deleted function" "" { target c++20 } 33 } + + std::tuple t2(1, 2L); + // { dg-error "here" "" { target { c++17_down && hosted } } 37 } + // { dg-error "use of deleted function" "" { target c++20 } 37 } + + std::tuple t3(1L, 2L); + // { dg-error "here" "" { target { c++17_down && hosted } } 41 } + // { dg-error "use of deleted function" "" { target c++20 } 41 } + + std::tuple t4(std::pair{}); + // { dg-error "here" "" { target { c++17_down && hosted } } 45 } + // { dg-error "use of deleted function" "" { target c++20 } 45 } + + std::pair p; + std::tuple t5(p); + // { dg-error "here" "" { target { c++17_down && hosted } } 50 } + // { dg-error "use of deleted function" "" { target c++20 } 50 } +} + +void +test_converting_ctors() +{ + std::tuple t0; + + std::tuple t1(t0); + // { dg-error "here" "" { target { c++17_down && hosted } } 60 } + // { dg-error "use of deleted function" "" { target c++20 } 60 } + + std::tuple t2(t0); + // { dg-error "here" "" { target { c++17_down && hosted } } 64 } + // { dg-error "use of deleted function" "" { target c++20 } 64 } + + std::tuple t3(t0); + // { dg-error "here" "" { target { c++17_down && hosted } } 68 } + // { dg-error "use of deleted function" "" { target c++20 } 68 } + + std::tuple t4(std::move(t0)); + // { dg-error "here" "" { target { c++17_down && hosted } } 72 } + // { dg-error "use of deleted function" "" { target c++20 } 72 } + + std::tuple t5(std::move(t0)); + // { dg-error "here" "" { target { c++17_down && hosted } } 76 } + // { dg-error "use of deleted function" "" { target c++20 } 76 } + + std::tuple t6(std::move(t0)); + // { dg-error "here" "" { target { c++17_down && hosted } } 80 } + // { dg-error "use of deleted function" "" { target c++20 } 80 } + + std::pair p0; + std::tuple t7(p0); + // { dg-error "here" "" { target { c++17_down && hosted } } 85 } + // { dg-error "use of deleted function" "" { target c++20 } 85 } + + std::tuple t8(p0); + // { dg-error "here" "" { target { c++17_down && hosted } } 89 } + // { dg-error "use of deleted function" "" { target c++20 } 89 } + + std::tuple t9(std::move(p0)); + // { dg-error "here" "" { target { c++17_down && hosted } } 93 } + // { dg-error "use of deleted function" "" { target c++20 } 93 } + + std::tuple t10(std::move(p0)); + // { dg-error "here" "" { target { c++17_down && hosted } } 97 } + // { dg-error "use of deleted function" "" { target c++20 } 97 } +} + +// TODO: test allocator-extended ctors +// TODO test 1-tuple or 3-tuple, not just 2-tuple + +// { dg-error "static assert.* dangling reference" "" { target { c++17_down && hosted } } 0 }