From patchwork Thu Oct 14 08:08:25 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jonathan Wakely X-Patchwork-Id: 46200 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 938D63858004 for ; Thu, 14 Oct 2021 08:11:17 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 938D63858004 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1634199077; bh=mN4sxgaz264DyryokSlrWLJoxjjA2LqfLIxhSQv4F1o=; h=Date:To:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=N/8pDj+tGFkTBj+W56PU3fwndGIKw5nr6VvilrFEpoMOfvrXK6ZUTlJjomISJBCje T7l9jGf5jFzf+CLOglx9LavtQiEekdCCpyb6T2+qzdxXOcsPWV6TFWaVXbMoiPUFSw uvc0alIwrkCB5eoZIWAFsFSU2PtWobbimJk48d9Y= 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 [216.205.24.124]) by sourceware.org (Postfix) with ESMTP id 86BFE3858C2C for ; Thu, 14 Oct 2021 08:08:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 86BFE3858C2C 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-413-DaXnhCRHNReSMzA3dA5GtQ-1; Thu, 14 Oct 2021 04:08:28 -0400 X-MC-Unique: DaXnhCRHNReSMzA3dA5GtQ-1 Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.phx2.redhat.com [10.5.11.13]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx01.redhat.com (Postfix) with ESMTPS id 1F4D819057B2; Thu, 14 Oct 2021 08:08:27 +0000 (UTC) Received: from localhost (unknown [10.33.36.131]) by smtp.corp.redhat.com (Postfix) with ESMTP id 9BC4A60936; Thu, 14 Oct 2021 08:08:26 +0000 (UTC) Date: Thu, 14 Oct 2021 09:08:25 +0100 To: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org Subject: [committed] libstdc++: Add missing constexpr to std::optional (P2231R1) Message-ID: MIME-Version: 1.0 X-Clacks-Overhead: GNU Terry Pratchett X-Scanned-By: MIMEDefang 2.79 on 10.5.11.13 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Disposition: inline X-Spam-Status: No, score=-13.8 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_NONE, TXREP autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) 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+patchwork=sourceware.org@gcc.gnu.org Sender: "Gcc-patches" This implements the changes in P2231R1 which make std::optional fully constexpr in C++20. libstdc++-v3/ChangeLog: * include/bits/stl_construct.h (_Construct): Use std::construct_at when constant evaluated. * include/std/optional (_Storage, _Optional_payload, optional): Add constexpr as specified by P2231R1. * include/std/version (__cpp_lib_optional): Update value for C++20. * testsuite/20_util/optional/requirements.cc: Check feature test macro. * testsuite/20_util/optional/constexpr/assign.cc: New test. * testsuite/20_util/optional/constexpr/cons/conv.cc: New test. * testsuite/20_util/optional/constexpr/modifiers.cc: New test. * testsuite/20_util/optional/constexpr/swap.cc: New test. * testsuite/20_util/optional/version.cc: New test. Tested powerpc64le-linux. Committed to trunk. commit 476f305b6cf11deec79a55cd5d30e1c13fad5bc0 Author: Jonathan Wakely Date: Wed Oct 13 22:32:28 2021 libstdc++: Add missing constexpr to std::optional (P2231R1) This implements the changes in P2231R1 which make std::optional fully constexpr in C++20. libstdc++-v3/ChangeLog: * include/bits/stl_construct.h (_Construct): Use std::construct_at when constant evaluated. * include/std/optional (_Storage, _Optional_payload, optional): Add constexpr as specified by P2231R1. * include/std/version (__cpp_lib_optional): Update value for C++20. * testsuite/20_util/optional/requirements.cc: Check feature test macro. * testsuite/20_util/optional/constexpr/assign.cc: New test. * testsuite/20_util/optional/constexpr/cons/conv.cc: New test. * testsuite/20_util/optional/constexpr/modifiers.cc: New test. * testsuite/20_util/optional/constexpr/swap.cc: New test. * testsuite/20_util/optional/version.cc: New test. diff --git a/libstdc++-v3/include/bits/stl_construct.h b/libstdc++-v3/include/bits/stl_construct.h index ad75eca69c2..e53ed0d9f91 100644 --- a/libstdc++-v3/include/bits/stl_construct.h +++ b/libstdc++-v3/include/bits/stl_construct.h @@ -88,7 +88,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION __location->~_Tp(); } -#if __cplusplus > 201703L +#if __cplusplus >= 202002L template constexpr auto construct_at(_Tp* __location, _Args&&... __args) @@ -104,9 +104,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION */ #if __cplusplus >= 201103L template + _GLIBCXX20_CONSTEXPR inline void _Construct(_Tp* __p, _Args&&... __args) - { ::new(static_cast(__p)) _Tp(std::forward<_Args>(__args)...); } + { +#if __cplusplus >= 202002L && __has_builtin(__builtin_is_constant_evaluated) + if (__builtin_is_constant_evaluated()) + { + // Allow std::_Construct to be used in constant expressions. + std::construct_at(__p, std::forward<_Args>(__args)...); + return; + } +#endif + ::new(static_cast(__p)) _Tp(std::forward<_Args>(__args)...); + } #else template inline void diff --git a/libstdc++-v3/include/std/optional b/libstdc++-v3/include/std/optional index b6ebe12b3e1..b69268b3642 100644 --- a/libstdc++-v3/include/std/optional +++ b/libstdc++-v3/include/std/optional @@ -40,6 +40,7 @@ #include #include #include +#include // _Construct #include // in_place_t #if __cplusplus > 201703L # include @@ -54,7 +55,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION * @{ */ -#define __cpp_lib_optional 201606L +#if __cplusplus == 201703L +# define __cpp_lib_optional 201606L +#else +# define __cpp_lib_optional 202106L +#endif template class optional; @@ -228,7 +233,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION { } // User-provided destructor is needed when _Up has non-trivial dtor. - ~_Storage() { } + _GLIBCXX20_CONSTEXPR ~_Storage() { } _Empty_byte _M_empty; _Up _M_value; @@ -239,12 +244,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION bool _M_engaged = false; template - void + constexpr void _M_construct(_Args&&... __args) noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>) { - ::new ((void *) std::__addressof(this->_M_payload)) - _Stored_type(std::forward<_Args>(__args)...); + std::_Construct(std::__addressof(this->_M_payload._M_value), + std::forward<_Args>(__args)...); this->_M_engaged = true; } @@ -393,7 +398,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _Optional_payload& operator=(_Optional_payload&&) = default; // Destructor needs to destroy the contained value: - ~_Optional_payload() { this->_M_reset(); } + _GLIBCXX20_CONSTEXPR ~_Optional_payload() { this->_M_reset(); } }; // Common base class for _Optional_base to avoid repeating these @@ -407,17 +412,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // The _M_construct operation has !_M_engaged as a precondition // while _M_destruct has _M_engaged as a precondition. template - void + constexpr void _M_construct(_Args&&... __args) noexcept(is_nothrow_constructible_v<_Stored_type, _Args...>) { - ::new - (std::__addressof(static_cast<_Dp*>(this)->_M_payload._M_payload)) - _Stored_type(std::forward<_Args>(__args)...); - static_cast<_Dp*>(this)->_M_payload._M_engaged = true; + static_cast<_Dp*>(this)->_M_payload._M_construct( + std::forward<_Args>(__args)...); } - void + constexpr void _M_destruct() noexcept { static_cast<_Dp*>(this)->_M_payload._M_destroy(); } @@ -782,7 +785,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Assignment operators. - optional& + _GLIBCXX20_CONSTEXPR optional& operator=(nullopt_t) noexcept { this->_M_reset(); @@ -790,6 +793,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template + _GLIBCXX20_CONSTEXPR enable_if_t<__and_v<__not_self<_Up>, __not_<__and_, is_same<_Tp, decay_t<_Up>>>>, @@ -809,6 +813,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template + _GLIBCXX20_CONSTEXPR enable_if_t<__and_v<__not_>, is_constructible<_Tp, const _Up&>, is_assignable<_Tp&, const _Up&>, @@ -834,6 +839,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template + _GLIBCXX20_CONSTEXPR enable_if_t<__and_v<__not_>, is_constructible<_Tp, _Up>, is_assignable<_Tp&, _Up>, @@ -860,6 +866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template + _GLIBCXX20_CONSTEXPR enable_if_t, _Tp&> emplace(_Args&&... __args) noexcept(is_nothrow_constructible_v<_Tp, _Args...>) @@ -870,6 +877,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template + _GLIBCXX20_CONSTEXPR enable_if_t&, _Args...>, _Tp&> emplace(initializer_list<_Up> __il, _Args&&... __args) @@ -884,7 +892,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Destructor is implicit, implemented in _Optional_base. // Swap. - void + _GLIBCXX20_CONSTEXPR void swap(optional& __other) noexcept(is_nothrow_move_constructible_v<_Tp> && is_nothrow_swappable_v<_Tp>) @@ -994,7 +1002,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return static_cast<_Tp>(std::forward<_Up>(__u)); } - void reset() noexcept { this->_M_reset(); } + _GLIBCXX20_CONSTEXPR void reset() noexcept { this->_M_reset(); } }; template @@ -1251,6 +1259,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // _GLIBCXX_RESOLVE_LIB_DEFECTS // 2748. swappable traits for optionals template + _GLIBCXX20_CONSTEXPR inline enable_if_t && is_swappable_v<_Tp>> swap(optional<_Tp>& __lhs, optional<_Tp>& __rhs) noexcept(noexcept(__lhs.swap(__rhs))) diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version index 24b86e0fa63..a395c05db2d 100644 --- a/libstdc++-v3/include/std/version +++ b/libstdc++-v3/include/std/version @@ -157,7 +157,9 @@ #define __cpp_lib_node_extract 201606 #define __cpp_lib_nonmember_container_access 201411 #define __cpp_lib_not_fn 201603 -#define __cpp_lib_optional 201606L +#if __cplusplus == 201703L // N.B. updated value in C++20 +# define __cpp_lib_optional 201606L +#endif #define __cpp_lib_parallel_algorithm 201603L #define __cpp_lib_raw_memory_algorithms 201606L #define __cpp_lib_sample 201603 @@ -255,6 +257,7 @@ # define __cpp_lib_make_obj_using_allocator 201811L #endif #define __cpp_lib_math_constants 201907L +#define __cpp_lib_optional 202106L #define __cpp_lib_polymorphic_allocator 201902L #if __cpp_lib_concepts # define __cpp_lib_ranges 202106L diff --git a/libstdc++-v3/testsuite/20_util/optional/constexpr/assign.cc b/libstdc++-v3/testsuite/20_util/optional/constexpr/assign.cc new file mode 100644 index 00000000000..fb82233052d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/constexpr/assign.cc @@ -0,0 +1,94 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } + +#include +#include + + +constexpr bool +test_assign() +{ + std::optional oi(1); + std::optional ou(2u), ou3(3u); + + // optional& operator=(nullopt_t); + oi = std::nullopt; + VERIFY( ! oi.has_value() ); + oi = std::nullopt; + VERIFY( ! oi.has_value() ); + + struct S { + constexpr S() { } + constexpr S(char, int, unsigned) { } + }; + std::optional os1, os2; + + // template optional& operator=(U&&); + os1 = {'0', 1, 2u}; + VERIFY( os1.has_value() ); + os2 = {'3', 4, 5u}; + VERIFY( os2.has_value() ); + oi = 0u; + VERIFY( *oi == 0 ); + oi = 1u; + VERIFY( *oi == 1 ); + + // template optional& operator=(const optional&); + oi = ou; + VERIFY( *oi == 2 ); + oi = ou3; + VERIFY( *oi == 3 ); + + // template optional& operator=(optional&&); + oi = std::move(ou); + VERIFY( *oi == 2 ); + oi = std::move(ou); + VERIFY( *oi == 2 ); + oi = std::move(ou3); + VERIFY( *oi == 3 ); + + return true; +} + +static_assert( test_assign() ); + +constexpr bool +test_emplace() +{ + struct S + { + constexpr S(int i) : val(i) { } + constexpr S(int i, int j) : val(i + j) { } + constexpr S(std::initializer_list l, int i = 0) : val(i) + { + for (char c : l) + val -= c; + } + + int val; + + constexpr bool operator==(int i) const { return val == i; } + }; + + + std::optional os; + + // template constexpr T& emplace(Args&&...); + os.emplace(1); + VERIFY( *os == 1 ); + os.emplace(2); + VERIFY( *os == 2 ); + os.emplace(2, 3); + VERIFY( *os == 5 ); + + // template + // constexpr T& emplace(initializer_list, Args&&...); + os.emplace({'3', '4', '5'}); + VERIFY( *os == -156 ); + os.emplace({'6', '7', '8'}, 25); + VERIFY( *os == -140 ); + + return true; +} + +static_assert( test_emplace() ); diff --git a/libstdc++-v3/testsuite/20_util/optional/constexpr/cons/conv.cc b/libstdc++-v3/testsuite/20_util/optional/constexpr/cons/conv.cc new file mode 100644 index 00000000000..cc638148d25 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/constexpr/cons/conv.cc @@ -0,0 +1,22 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } + +#include +#include + +constexpr bool +test_cons() +{ + std::optional oi(1); + std::optional ol(oi); + VERIFY( *ol == 1L ); + VERIFY( *oi == 1 ); + + std::optional ou(std::move(oi)); + VERIFY( *ou == 1u ); + VERIFY( oi.has_value() && *oi == 1 ); + + return true; +} + +static_assert( test_cons() ); diff --git a/libstdc++-v3/testsuite/20_util/optional/constexpr/modifiers.cc b/libstdc++-v3/testsuite/20_util/optional/constexpr/modifiers.cc new file mode 100644 index 00000000000..614607d0216 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/constexpr/modifiers.cc @@ -0,0 +1,19 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } + +#include +#include + +constexpr bool +test_reset() +{ + std::optional oi(1); + oi.reset(); + VERIFY( ! oi.has_value() ); + oi.reset(); + VERIFY( ! oi.has_value() ); + + return true; +} + +static_assert( test_reset() ); diff --git a/libstdc++-v3/testsuite/20_util/optional/constexpr/swap.cc b/libstdc++-v3/testsuite/20_util/optional/constexpr/swap.cc new file mode 100644 index 00000000000..2d18a51106c --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/constexpr/swap.cc @@ -0,0 +1,29 @@ +// { dg-options "-std=gnu++20" } +// { dg-do compile { target c++20 } } + +#include +#include + +constexpr bool +test_swap() +{ + std::optional o0, o1(1); + o0.swap(o1); + VERIFY( *o0 == 1 ); + VERIFY( ! o1.has_value() ); + o0.swap(o1); + VERIFY( ! o0.has_value() ); + VERIFY( *o1 == 1 ); + o0.swap(o0); + VERIFY( ! o0.has_value() ); + o1.swap(o1); + VERIFY( *o1 == 1 ); + std::optional o2(2); + swap(o1, o2); + VERIFY( *o1 == 2 ); + VERIFY( *o2 == 1 ); + + return true; +} + +static_assert( test_swap() ); diff --git a/libstdc++-v3/testsuite/20_util/optional/requirements.cc b/libstdc++-v3/testsuite/20_util/optional/requirements.cc index 550c0c4eac0..c24bd140351 100644 --- a/libstdc++-v3/testsuite/20_util/optional/requirements.cc +++ b/libstdc++-v3/testsuite/20_util/optional/requirements.cc @@ -18,6 +18,15 @@ // . #include + +#ifndef __cpp_lib_optional +# error "Feature test macro for optional is missing in " +#elif __cpp_lib_optional < 201606L +# error "Feature test macro for optional has wrong value in " +#elif __cplusplus >= 202002L && __cpp_lib_optional < 202106L +# error "Feature test macro for optional has wrong value for C++20 in " +#endif + #include #include diff --git a/libstdc++-v3/testsuite/20_util/optional/version.cc b/libstdc++-v3/testsuite/20_util/optional/version.cc new file mode 100644 index 00000000000..d8c9851f28f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/optional/version.cc @@ -0,0 +1,11 @@ +// { dg-do compile { target c++17 } } + +#include + +#ifndef __cpp_lib_optional +# error "Feature test macro for optional is missing in " +#elif __cpp_lib_optional < 201606L +# error "Feature test macro for optional has wrong value in " +#elif __cplusplus >= 202002L && __cpp_lib_optional < 202106L +# error "Feature test macro for optional has wrong value for C++20 in " +#endif