From patchwork Wed Sep 28 23:35:54 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 1515 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:adf:f2ce:0:0:0:0:0 with SMTP id d14csp423140wrp; Wed, 28 Sep 2022 16:37:44 -0700 (PDT) X-Google-Smtp-Source: AMsMyM7lO5X+85xf4zabn6GUDgPWYou+tLqs1qWKtWGLgqS70fKRtyGqbsIqAbmEEFJdGW/dpHa4 X-Received: by 2002:a05:6402:5189:b0:451:791e:f328 with SMTP id q9-20020a056402518900b00451791ef328mr463548edd.282.1664408264327; Wed, 28 Sep 2022 16:37:44 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1664408264; cv=none; d=google.com; s=arc-20160816; b=I/yV9UWe3qySCONmIVtgnKGW2NmN16jGm4pC1l6zninZ3NveiUb9TszdbEfygDM71T IOf2+NLHylwGSP1SZvVPXNIE3AimGNE6KWCsYPx2AtpRijg4tWCfHhkJduD/Wsj+De/s RrpUgtnBchLmviMveoXBUHbslKbtpH+l3DkxEzbztcX9x81FeO7KKOj6GZSlcnuTkfGn cytGAaIC677UkNwHcHW38PYLyOX0raXvYHXz4u1cv3JW5JLJ1FbNQsv+75F+4md1+3s2 /zGtHcUSSga8bG74+4ukwZPvUtKesnf78zUANUfXBUOpFHPTqe7kr9ay4fSnyfmK80Dc VMLA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:reply-to:from:list-subscribe:list-help:list-post :list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:mime-version:message-id:date:subject:to :dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=lPnjCNikyqYY8hil7Bday3sNi/jqC1kq2ZPfk8R/bMg=; b=RjvWjktgRtCgBP11NiMznVTZ1+CcuOV0Fctg7eOV3mSudaquu+QND1GveWqf6V7yc+ dVrz2N1ZT1USsmxd68UtxvGrszQRoSgtF8EZRVQynZLDNxya5lm2V3m2awP4q5jz6aAk 4I4a2gs2oohImiZBefqnZm7fI+ZP1G8Vw7lpY5M6NkOMbSF3aGCFTol4f5vTpGl+1Mdu miiIlD7eoiHVBeuILAyUAETJc8lxjvhEWlQetW1aDcuOm+01zX/Mda229eo5n8cHjH5N 92zsNuqBXwRqdNg3aDX+45WDIOpiSg3cu0PfuZDInMhn0vS+WSq9IquY2q7sDCGJG+ur XuFw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=vK49uJ7d; 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=gnu.org Received: from sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id g9-20020a056402090900b0043b9617f589si7689328edz.209.2022.09.28.16.37.44 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 28 Sep 2022 16:37:44 -0700 (PDT) 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=@gcc.gnu.org header.s=default header.b=vK49uJ7d; 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=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id AFCEE3857357 for ; Wed, 28 Sep 2022 23:37:29 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org AFCEE3857357 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1664408250; bh=lPnjCNikyqYY8hil7Bday3sNi/jqC1kq2ZPfk8R/bMg=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=vK49uJ7doBHfjvXC+qf14tFd+9tpoiNaxVc3kABGqTHLmHEqutlI/YJdrRZ698AzE fC0OS/fNqFz2FHAz+hgYL5AwVGgoFw2Hoouh/ZQsslEvPyE3ZJ51c2INQRfKOngUXc vcnVcS8ry+rY3qJM39sZJ+vB1rKUxCANLDrsWETw= 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.129.124]) by sourceware.org (Postfix) with ESMTPS id 163D13858009 for ; Wed, 28 Sep 2022 23:35:59 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 163D13858009 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-141-eJB3sPDuNNywHPKhJ6QnEg-1; Wed, 28 Sep 2022 19:35:55 -0400 X-MC-Unique: eJB3sPDuNNywHPKhJ6QnEg-1 Received: from smtp.corp.redhat.com (int-mx01.intmail.prod.int.rdu2.redhat.com [10.11.54.1]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 5DF59185A794; Wed, 28 Sep 2022 23:35:55 +0000 (UTC) Received: from localhost (unknown [10.33.36.214]) by smtp.corp.redhat.com (Postfix) with ESMTP id 0ACD140C206B; Wed, 28 Sep 2022 23:35:54 +0000 (UTC) To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Make INVOKE refuse to create dangling references [PR70692] Date: Thu, 29 Sep 2022 00:35:54 +0100 Message-Id: <20220928233554.2670010-1-jwakely@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.1 on 10.11.54.1 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.4 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_LOW, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=unavailable 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.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Jonathan Wakely via Gcc-patches From: Jonathan Wakely Reply-To: Jonathan Wakely Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1745258560386019870?= X-GMAIL-MSGID: =?utf-8?q?1745258560386019870?= Tested powerpc64le-linux. Pushed to trunk. -- >8 -- This is the next part of the library changes from P2255R2. This makes INVOKE ill-formed if converting the INVOKE expression to R would bind a reference to a temporary object. The is_invocable_r trait is now false if the invocation would create a dangling reference. This is done by adding the dangling check to the __is_invocable_impl partial specialization used for INVOKE expressions. This change also slightly simplifies the nothrow checking recently added to that partial specialization. This change also removes the is_invocable_r checks from the pre-C++17 implementation of std::__invoke_r, because there is no need for it to be SFINAE-friendly. None of our C++11 and C++14 uses of INVOKE require those constraints. The std::function constructor needs to check is_invocable_r, but that's already done explicitly, so we don't need to recheck when calling __is_invoke_r in std::function::operator(). The other uses of std::__is_invoke_r do not need to be constrained and can just be ill-formed if the INVOKE expression is ill-formed. libstdc++-v3/ChangeLog: PR libstdc++/70692 * include/bits/invoke.h [__cplusplus < 201703] (__invoke_r): Remove is_invocable and is_convertible constraints. * include/std/type_traits (__is_invocable_impl::_S_conv): Use non-deduced context for parameter. (__is_invocable_impl::_S_test): Remove _Check_noex template parameter and use deduced noexcept value in its place. Add bool parameter to detect dangling references. (__is_invocable_impl::type): Adjust call to _S_test to avoid deducing unnecessary noexcept property.. (__is_invocable_impl::__nothrow_type): Rename to ... (__is_invocable_impl::__nothrow_conv): ... this. Adjust call to _S_test to deduce noexcept property. * testsuite/20_util/bind/dangling_ref.cc: New test. * testsuite/20_util/function/cons/70692.cc: New test. * testsuite/20_util/function_objects/invoke/dangling_ref.cc: New test. * testsuite/20_util/is_invocable/dangling_ref.cc: New test. * testsuite/30_threads/packaged_task/cons/dangling_ref.cc: New test. --- libstdc++-v3/include/bits/invoke.h | 30 ++++++++----------- libstdc++-v3/include/std/type_traits | 27 ++++++++++------- .../testsuite/20_util/bind/dangling_ref.cc | 9 ++++++ .../testsuite/20_util/function/cons/70692.cc | 13 ++++++++ .../function_objects/invoke/dangling_ref.cc | 12 ++++++++ .../20_util/is_invocable/dangling_ref.cc | 6 ++++ .../packaged_task/cons/dangling_ref.cc | 11 +++++++ 7 files changed, 80 insertions(+), 28 deletions(-) create mode 100644 libstdc++-v3/testsuite/20_util/bind/dangling_ref.cc create mode 100644 libstdc++-v3/testsuite/20_util/function/cons/70692.cc create mode 100644 libstdc++-v3/testsuite/20_util/function_objects/invoke/dangling_ref.cc create mode 100644 libstdc++-v3/testsuite/20_util/is_invocable/dangling_ref.cc create mode 100644 libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc diff --git a/libstdc++-v3/include/bits/invoke.h b/libstdc++-v3/include/bits/invoke.h index cdecca0e2bf..8724a764f73 100644 --- a/libstdc++-v3/include/bits/invoke.h +++ b/libstdc++-v3/include/bits/invoke.h @@ -115,29 +115,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::forward<_Callable>(__fn), std::forward<_Args>(__args)...); } -#else // C++11 - template - using __can_invoke_as_void = __enable_if_t< - __and_, __is_invocable<_Callable, _Args...>>::value, - _Res - >; - - template - using __can_invoke_as_nonvoid = __enable_if_t< - __and_<__not_>, - is_convertible::type, - _Res> - >::value, - _Res - >; +#else // C++11 or C++14 + // This is a non-SFINAE-friendly std::invoke_r(fn, args...) for C++11/14. + // It's used in std::function, std::bind, and std::packaged_task. Only + // std::function is constrained on is_invocable_r, but that is checked on + // construction so doesn't need to be checked again when calling __invoke_r. + // Consequently, these __invoke_r overloads do not check for invocable + // arguments, nor check that the invoke result is convertible to R. // INVOKE: Invoke a callable object and convert the result to R. template - constexpr __can_invoke_as_nonvoid<_Res, _Callable, _Args...> + constexpr __enable_if_t::value, _Res> __invoke_r(_Callable&& __fn, _Args&&... __args) { using __result = __invoke_result<_Callable, _Args...>; using __type = typename __result::type; + static_assert(!__reference_converts_from_temporary(_Res, __type), + "INVOKE must not create a dangling reference"); using __tag = typename __result::__invoke_type; return std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn), std::forward<_Args>(__args)...); @@ -145,7 +139,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // INVOKE when R is cv void template - _GLIBCXX14_CONSTEXPR __can_invoke_as_void<_Res, _Callable, _Args...> + _GLIBCXX14_CONSTEXPR __enable_if_t::value, _Res> __invoke_r(_Callable&& __fn, _Args&&... __args) { using __result = __invoke_result<_Callable, _Args...>; @@ -154,7 +148,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION std::__invoke_impl<__type>(__tag{}, std::forward<_Callable>(__fn), std::forward<_Args>(__args)...); } -#endif // C++11 +#endif // C++11 or C++14 _GLIBCXX_END_NAMESPACE_VERSION } // namespace std diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 1ac805152d4..22c1af26397 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -2864,7 +2864,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __is_invocable_impl : false_type { - using __nothrow_type = false_type; // For is_nothrow_invocable_r + using __nothrow_conv = false_type; // For is_nothrow_invocable_r }; // Used for valid INVOKE and INVOKE expressions. @@ -2874,7 +2874,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __void_t> : true_type { - using __nothrow_type = true_type; // For is_nothrow_invocable_r + using __nothrow_conv = true_type; // For is_nothrow_invocable_r }; #pragma GCC diagnostic push @@ -2887,18 +2887,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { private: // The type of the INVOKE expression. + using _Res_t = typename _Result::type; + // Unlike declval, this doesn't add_rvalue_reference, so it respects // guaranteed copy elision. - static typename _Result::type _S_get() noexcept; + static _Res_t _S_get() noexcept; + // Used to check if _Res_t can implicitly convert to _Tp. template - static void _S_conv(_Tp) noexcept; + static void _S_conv(__type_identity_t<_Tp>) noexcept; // This overload is viable if INVOKE(f, args...) can convert to _Tp. - template(_S_get())), typename = decltype(_S_conv<_Tp>(_S_get())), - bool _Noex = noexcept(_S_conv<_Tp>(_S_get()))> - static __bool_constant<_Check_Noex ? _Noex : true> + bool _Dangle = __reference_converts_from_temporary(_Tp, _Res_t)> + static __bool_constant<_Nothrow && !_Dangle> _S_test(int); template @@ -2907,10 +2911,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION public: // For is_invocable_r - using type = decltype(_S_test<_Ret>(1)); + using type = decltype(_S_test<_Ret, /* Nothrow = */ true>(1)); // For is_nothrow_invocable_r - using __nothrow_type = decltype(_S_test<_Ret, true>(1)); + using __nothrow_conv = decltype(_S_test<_Ret>(1)); }; #pragma GCC diagnostic pop @@ -3041,9 +3045,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; /// @cond undocumented + // This checks that the INVOKE expression is well-formed and that the + // conversion to R does not throw. It does *not* check whether the INVOKE + // expression itself can throw. That is done by __call_is_nothrow_ instead. template using __is_nt_invocable_impl - = typename __is_invocable_impl<_Result, _Ret>::__nothrow_type; + = typename __is_invocable_impl<_Result, _Ret>::__nothrow_conv; /// @endcond /// std::is_nothrow_invocable_r diff --git a/libstdc++-v3/testsuite/20_util/bind/dangling_ref.cc b/libstdc++-v3/testsuite/20_util/bind/dangling_ref.cc new file mode 100644 index 00000000000..70393e4392f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bind/dangling_ref.cc @@ -0,0 +1,9 @@ +// { dg-do compile { target c++11 } } +#include + +int f(); +auto b = std::bind(f); +int i = b(); // { dg-error "here" "" { target { c++14_down } } } +// { dg-error "dangling reference" "" { target { c++14_down } } 0 } +// { dg-error "no matching function" "" { target c++17 } 0 } +// { dg-error "enable_if" "" { target c++17 } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function/cons/70692.cc b/libstdc++-v3/testsuite/20_util/function/cons/70692.cc new file mode 100644 index 00000000000..7cdc472497e --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function/cons/70692.cc @@ -0,0 +1,13 @@ +// { dg-do compile { target c++11 } } +// PR libstdc++/70692 +// No warning when function binds a reference to a temporary +#include + +int f(); + +int main() +{ + std::function ff(f); // { dg-error "no matching function" } + std::function f2(f); // { dg-error "no matching function" } +} +// { dg-error "std::enable_if" "" { target *-*-* } 0 } diff --git a/libstdc++-v3/testsuite/20_util/function_objects/invoke/dangling_ref.cc b/libstdc++-v3/testsuite/20_util/function_objects/invoke/dangling_ref.cc new file mode 100644 index 00000000000..1513480bd8f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/invoke/dangling_ref.cc @@ -0,0 +1,12 @@ +// { dg-options "-std=gnu++23" } +// { dg-do compile { target c++23 } } +#include + +int f(); + +template +concept can_invoke = requires (int (&f)()) { std::invoke_r(f); }; + +static_assert( not can_invoke ); +static_assert( not can_invoke ); +static_assert( not can_invoke ); diff --git a/libstdc++-v3/testsuite/20_util/is_invocable/dangling_ref.cc b/libstdc++-v3/testsuite/20_util/is_invocable/dangling_ref.cc new file mode 100644 index 00000000000..46719b9bd95 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_invocable/dangling_ref.cc @@ -0,0 +1,6 @@ +// { dg-do compile { target c++17 } } +#include + +static_assert( not std::is_invocable_r_v ); +static_assert( not std::is_invocable_r_v ); +static_assert( not std::is_invocable_r_v ); diff --git a/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc new file mode 100644 index 00000000000..e9edb5edc8b --- /dev/null +++ b/libstdc++-v3/testsuite/30_threads/packaged_task/cons/dangling_ref.cc @@ -0,0 +1,11 @@ +// { dg-do compile { target c++11 } } +#include + +// C++20 [futures.task.members] +// Mandates: is_invocable_r_v is true. + +int f(); +std::packaged_task task(f); +// { dg-error "dangling reference" "" { target { c++14_down } } 0 } +// { dg-error "no matching function" "" { target c++17 } 0 } +// { dg-error "enable_if" "" { target c++17 } 0 }