From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) by sourceware.org (Postfix) with ESMTP id 182C53984005 for ; Mon, 28 Jun 2021 13:24:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 182C53984005 Received: from mimecast-mx01.redhat.com (mimecast-mx01.redhat.com [209.132.183.4]) (Using TLS) by relay.mimecast.com with ESMTP id us-mta-70-BxXMqrWxOVyVAlKJvbrAag-1; Mon, 28 Jun 2021 09:24:02 -0400 X-MC-Unique: BxXMqrWxOVyVAlKJvbrAag-1 Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id D50A6102CB2E; Mon, 28 Jun 2021 13:24:01 +0000 (UTC) Received: from localhost (unknown [10.33.36.175]) by smtp.corp.redhat.com (Postfix) with ESMTP id 7B7925D6AD; Mon, 28 Jun 2021 13:24:01 +0000 (UTC) Date: Mon, 28 Jun 2021 14:24:00 +0100 From: Jonathan Wakely To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Allow unique_ptr::operator[] [PR 101236] Message-ID: MIME-Version: 1.0 X-Clacks-Overhead: GNU Terry Pratchett X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: multipart/mixed; boundary="qnG/EzXvdk0lSXuO" Content-Disposition: inline X-Spam-Status: No, score=-13.9 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, RCVD_IN_MSPIKE_H4, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_PASS, TXREP, URI_HEX autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libstdc++@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libstdc++ mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 28 Jun 2021 13:24:09 -0000 --qnG/EzXvdk0lSXuO Content-Type: text/plain; charset=us-ascii Content-Disposition: inline PR libstdc++/101236 shows that LLVM depends on being able to use unique_ptr::operator[] when T is incomplete. This is undefined, but previously worked with libstdc++. When I added the conditional noexcept to that operator we started to diagnose the incomplete type. This change restores support for that case, by making the noexcept condition check that the type is complete before checking whether indexing on the pointer can throw. A workaround for PR c++/101239 is needed to avoid a bogus error where G++ fails to do SFINAE on the ill-formed p[n] expression and gets an ICE. Instead of checking that the p[n] expression is valid in the trailing-return-type, we only check that the element_type is complete. libstdc++-v3/ChangeLog: PR libstdc++/101236 * include/bits/unique_ptr.h (unique_ptr::operator[]): Fail gracefully if element_type is incomplete. * testsuite/20_util/unique_ptr/cons/incomplete.cc: Clarify that the standard doesn't require this test to work for array types. * testsuite/20_util/unique_ptr/lwg2762.cc: Check that incomplete types can be used with array specialization. * testsuite/20_util/unique_ptr/101236.cc: New test. Tested powerpc64le-linux. Committed to trunk. --qnG/EzXvdk0lSXuO Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="patch.txt" commit b7a89c041aa1d67654f1ba7b2839e221c3e14748 Author: Jonathan Wakely Date: Mon Jun 28 12:59:19 2021 libstdc++: Allow unique_ptr::operator[] [PR 101236] PR libstdc++/101236 shows that LLVM depends on being able to use unique_ptr::operator[] when T is incomplete. This is undefined, but previously worked with libstdc++. When I added the conditional noexcept to that operator we started to diagnose the incomplete type. This change restores support for that case, by making the noexcept condition check that the type is complete before checking whether indexing on the pointer can throw. A workaround for PR c++/101239 is needed to avoid a bogus error where G++ fails to do SFINAE on the ill-formed p[n] expression and gets an ICE. Instead of checking that the p[n] expression is valid in the trailing-return-type, we only check that the element_type is complete. libstdc++-v3/ChangeLog: PR libstdc++/101236 * include/bits/unique_ptr.h (unique_ptr::operator[]): Fail gracefully if element_type is incomplete. * testsuite/20_util/unique_ptr/cons/incomplete.cc: Clarify that the standard doesn't require this test to work for array types. * testsuite/20_util/unique_ptr/lwg2762.cc: Check that incomplete types can be used with array specialization. * testsuite/20_util/unique_ptr/101236.cc: New test. diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h index 1781fe15649..e478056c755 100644 --- a/libstdc++-v3/include/bits/unique_ptr.h +++ b/libstdc++-v3/include/bits/unique_ptr.h @@ -491,6 +491,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION = __and_< is_base_of<_Tp, _Up>, __not_, __remove_cv<_Up>>> >; + // This checks whether p[n] is noexcept, but fails gracefully when + // element_type is incomplete. The standard requires a complete type + // for unique_ptr, but we try to support it anyway (PR 101236). + template + static constexpr auto + _S_nothrow_deref(size_t __n) + -> decltype(sizeof(_Elt) != 0) // PR c++/101239 + { return noexcept(std::declval<_Ptr>()[__n]); } + + template + static constexpr bool + _S_nothrow_deref(...) + { return false; } + public: using pointer = typename __uniq_ptr_impl<_Tp, _Dp>::pointer; using element_type = _Tp; @@ -655,7 +669,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION /// Access an element of owned array. typename std::add_lvalue_reference::type operator[](size_t __i) const - noexcept(noexcept(std::declval()[std::declval()])) + noexcept(_S_nothrow_deref(0)) { __glibcxx_assert(get() != pointer()); return get()[__i]; diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/101236.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/101236.cc new file mode 100644 index 00000000000..2f55f4baf9a --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/101236.cc @@ -0,0 +1,13 @@ +// { dg-do compile { target c++11 } } +#include + +struct Incomplete; +struct pr101236 +{ + // The standard says "T shall be a complete type" for unique_ptr + // so this is a GCC extension. + std::unique_ptr p; + + Incomplete& f() { return p[0]; } +}; +struct Incomplete { }; diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/incomplete.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/incomplete.cc index 879a1d021a1..6b55d5744ed 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/cons/incomplete.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/cons/incomplete.cc @@ -24,9 +24,17 @@ struct Incomplete; void f(void** p) { ::new (p[0]) std::unique_ptr(); - ::new (p[1]) std::unique_ptr(); // PR libstdc++/87704 - ::new (p[2]) std::unique_ptr(nullptr); - ::new (p[3]) std::unique_ptr(nullptr); + ::new (p[1]) std::unique_ptr(nullptr); +} + +// The standard says "T shall be a complete type" for unique_ptr +// so this is a GCC extension. +void f_array(void** p) +{ + ::new (p[0]) std::unique_ptr(); + + // PR libstdc++/87704 + ::new (p[1]) std::unique_ptr(nullptr); } diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/lwg2762.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg2762.cc index 3cc2ea6b87d..c88237dd9ea 100644 --- a/libstdc++-v3/testsuite/20_util/unique_ptr/lwg2762.cc +++ b/libstdc++-v3/testsuite/20_util/unique_ptr/lwg2762.cc @@ -32,12 +32,24 @@ template using UPtr = std::unique_ptr>; // noexcept-specifier depends on the pointer type -static_assert( noexcept(*std::declval&>()), "" ); -static_assert( ! noexcept(*std::declval&>()), "" ); +static_assert( noexcept(*std::declval&>()), "LWG 2762" ); +static_assert( ! noexcept(*std::declval&>()), "LWG 2762" ); // This has always been required, even in C++11. -static_assert( noexcept(std::declval&>().operator->()), "" ); +static_assert( noexcept(std::declval>().operator->()), + "operator-> is always noexcept" ); +static_assert( noexcept(std::declval&>().operator->()), + "operator-> is always noexcept" ); -// This is not required by the standard -static_assert( noexcept(std::declval&>()[0]), "" ); -static_assert( ! noexcept(std::declval&>()[0]), "" ); +// This is not required by the standard, but we make it depend on the pointer. +static_assert( noexcept(std::declval>()[0]), "QoI" ); +static_assert( noexcept(std::declval&>()[0]), "QoI" ); +static_assert( ! noexcept(std::declval&>()[0]), "QoI" ); + +// This is forbidden by the standard ("T shall be a complete type") +// but we try to support it anyway, see PR libstdc++/101236. +struct Incomplete; +static_assert( ! noexcept(std::declval>()[0]), + "this would be noexcept if the type was complete"); +static_assert( ! noexcept(std::declval>()[0]), + "this would still be noexcept(false) if the type was complete"); --qnG/EzXvdk0lSXuO--