* [committed] libstdc++: Add missing constexpr to std::optional (P2231R1)
@ 2021-10-14 8:08 Jonathan Wakely
0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2021-10-14 8:08 UTC (permalink / raw)
To: libstdc++, gcc-patches
[-- Attachment #1: Type: text/plain, Size: 807 bytes --]
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.
[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 14149 bytes --]
commit 476f305b6cf11deec79a55cd5d30e1c13fad5bc0
Author: Jonathan Wakely <jwakely@redhat.com>
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<typename _Tp, typename... _Args>
constexpr auto
construct_at(_Tp* __location, _Args&&... __args)
@@ -104,9 +104,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
*/
#if __cplusplus >= 201103L
template<typename _Tp, typename... _Args>
+ _GLIBCXX20_CONSTEXPR
inline void
_Construct(_Tp* __p, _Args&&... __args)
- { ::new(static_cast<void*>(__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<void*>(__p)) _Tp(std::forward<_Args>(__args)...);
+ }
#else
template<typename _T1, typename _T2>
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 <bits/enable_special_members.h>
#include <bits/exception_defines.h>
#include <bits/functional_hash.h>
+#include <bits/stl_construct.h> // _Construct
#include <bits/utility.h> // in_place_t
#if __cplusplus > 201703L
# include <compare>
@@ -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<typename _Tp>
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<typename... _Args>
- 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<T> 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<typename... _Args>
- 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<typename _Up = _Tp>
+ _GLIBCXX20_CONSTEXPR
enable_if_t<__and_v<__not_self<_Up>,
__not_<__and_<is_scalar<_Tp>,
is_same<_Tp, decay_t<_Up>>>>,
@@ -809,6 +813,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename _Up>
+ _GLIBCXX20_CONSTEXPR
enable_if_t<__and_v<__not_<is_same<_Tp, _Up>>,
is_constructible<_Tp, const _Up&>,
is_assignable<_Tp&, const _Up&>,
@@ -834,6 +839,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename _Up>
+ _GLIBCXX20_CONSTEXPR
enable_if_t<__and_v<__not_<is_same<_Tp, _Up>>,
is_constructible<_Tp, _Up>,
is_assignable<_Tp&, _Up>,
@@ -860,6 +866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _Args>
+ _GLIBCXX20_CONSTEXPR
enable_if_t<is_constructible_v<_Tp, _Args...>, _Tp&>
emplace(_Args&&... __args)
noexcept(is_nothrow_constructible_v<_Tp, _Args...>)
@@ -870,6 +877,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename _Up, typename... _Args>
+ _GLIBCXX20_CONSTEXPR
enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _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<typename _Tp>
@@ -1251,6 +1259,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2748. swappable traits for optionals
template<typename _Tp>
+ _GLIBCXX20_CONSTEXPR
inline enable_if_t<is_move_constructible_v<_Tp> && 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 <optional>
+#include <testsuite_hooks.h>
+
+
+constexpr bool
+test_assign()
+{
+ std::optional<int> oi(1);
+ std::optional<unsigned> 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<S> os1, os2;
+
+ // template<class U = T> 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<class U> optional& operator=(const optional<U>&);
+ oi = ou;
+ VERIFY( *oi == 2 );
+ oi = ou3;
+ VERIFY( *oi == 3 );
+
+ // template<class U> optional& operator=(optional<U>&&);
+ 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<char> 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<S> os;
+
+ // template<class... Args> constexpr T& emplace(Args&&...);
+ os.emplace(1);
+ VERIFY( *os == 1 );
+ os.emplace(2);
+ VERIFY( *os == 2 );
+ os.emplace(2, 3);
+ VERIFY( *os == 5 );
+
+ // template<class U, class... Args>
+ // constexpr T& emplace(initializer_list<U>, 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 <optional>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_cons()
+{
+ std::optional<int> oi(1);
+ std::optional<long> ol(oi);
+ VERIFY( *ol == 1L );
+ VERIFY( *oi == 1 );
+
+ std::optional<unsigned> 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 <optional>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_reset()
+{
+ std::optional<int> 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 <optional>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test_swap()
+{
+ std::optional<int> 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<int> 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 @@
// <http://www.gnu.org/licenses/>.
#include <optional>
+
+#ifndef __cpp_lib_optional
+# error "Feature test macro for optional is missing in <optional>"
+#elif __cpp_lib_optional < 201606L
+# error "Feature test macro for optional has wrong value in <optional>"
+#elif __cplusplus >= 202002L && __cpp_lib_optional < 202106L
+# error "Feature test macro for optional has wrong value for C++20 in <optional>"
+#endif
+
#include <testsuite_hooks.h>
#include <tuple>
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 <version>
+
+#ifndef __cpp_lib_optional
+# error "Feature test macro for optional is missing in <version>"
+#elif __cpp_lib_optional < 201606L
+# error "Feature test macro for optional has wrong value in <version>"
+#elif __cplusplus >= 202002L && __cpp_lib_optional < 202106L
+# error "Feature test macro for optional has wrong value for C++20 in <version>"
+#endif
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2021-10-14 8:08 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-14 8:08 [committed] libstdc++: Add missing constexpr to std::optional (P2231R1) Jonathan Wakely
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).