]> gcc.gnu.org Git - gcc.git/commitdiff
libstdc++: Add missing constexpr to std::optional (P2231R1)
authorJonathan Wakely <jwakely@redhat.com>
Wed, 13 Oct 2021 21:32:28 +0000 (22:32 +0100)
committerJonathan Wakely <jwakely@redhat.com>
Wed, 24 Nov 2021 11:24:59 +0000 (11:24 +0000)
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.

(cherry picked from commit 476f305b6cf11deec79a55cd5d30e1c13fad5bc0)

libstdc++-v3/include/bits/stl_construct.h
libstdc++-v3/include/std/optional
libstdc++-v3/include/std/version
libstdc++-v3/testsuite/20_util/optional/constexpr/assign.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/optional/constexpr/cons/conv.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/optional/constexpr/modifiers.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/optional/constexpr/swap.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/optional/requirements.cc
libstdc++-v3/testsuite/20_util/optional/version.cc [new file with mode: 0644]

index ad75eca69c203df775b168d2348c9acffbde8fac..e53ed0d9f912c20e244363c304c2f9cb01216378 100644 (file)
@@ -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
index fbabb52758e2451e743e0945aaede3ae4ff241ea..449c27a80c46e4d6888bcee098480d53937c5c23 100644 (file)
 #include <exception>
 #include <new>
 #include <initializer_list>
+#include <bits/enable_special_members.h>
 #include <bits/exception_defines.h>
 #include <bits/functional_hash.h>
-#include <bits/enable_special_members.h>
+#include <bits/stl_construct.h> // _Construct
 #if __cplusplus > 201703L
 # include <compare>
 #endif
@@ -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)))
index 5288cc55a0bf73ce36108e5f43a5cc5fa97aa2dd..c591398e877546d4f0b768a3f39bcd341ae02110 100644 (file)
 #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
 # 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 201911L
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 (file)
index 0000000..fb82233
--- /dev/null
@@ -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 (file)
index 0000000..cc63814
--- /dev/null
@@ -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 (file)
index 0000000..614607d
--- /dev/null
@@ -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 (file)
index 0000000..2d18a51
--- /dev/null
@@ -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() );
index 550c0c4eac0bd96947aec1ec2782962ee5b28f5f..c24bd14035105200ad8149af75c2f10917e7bc39 100644 (file)
 // <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 (file)
index 0000000..d8c9851
--- /dev/null
@@ -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
This page took 0.080923 seconds and 5 git commands to generate.