libstdc++: Add missing constexpr to std::variant (P2231R1)

This implements the changes in P2231R1 which make std::variant fully
constexpr in C++20.

We need to replace placement new with std::construct_at, but that isn't
defined for C++17. Use std::_Construct instead, which forwards to
std::construct_at in C++20 mode (since the related changes to make
std::optional fully constexpr, in r12-4389).

We also need to replace the untyped char buffer in _Uninitialized with a
union, which can be accessed in constexpr functions. But the union needs
to have a non-trivial destructor if its variant type is non-trivial,
which means that the _Variadic_union also needs a non-trivial
destructor. This adds a constrained partial specialization of
_Variadic_union for the C++20-only case where a non-trivial destructor
is needed.

We can't use concepts to constrain the specialization (or the primary
template's destructor) in C++17, so retain the untyped char buffer
solution for C++17 mode.

libstdc++-v3/ChangeLog:

	* include/std/variant (__cpp_lib_variant): Update value for
	C++20.
	(__variant_cast, __variant_construct): Add constexpr for C++20.
	(__variant_construct_single, __construct_by_index) Likewise. Use
	std::_Construct instead of placement new.
	(_Uninitialized<T, false>) [__cplusplus >= 202002]: Replace
	buffer with a union and define a destructor.
	(_Variadic_union) [__cplusplus >= 202002]: Add a specialization
	for non-trivial destruction.
	(_Variant_storage::__index_of): New helper variable template.
	(_Variant_storage::~_Variant_storage()): Add constexpr.
	(_Variant_storage::_M_reset()): Likewise.
	(_Copy_ctor_base, _Move_ctor_base): Likewise.
	(_Copy_assign_base, _Move_assign_base): Likewise.
	(variant, swap): Likewise.
	* include/std/version (__cpp_lib_variant): Update value for
	C++20.
	* testsuite/20_util/optional/version.cc: Check for exact value
	in C++17.
	* testsuite/20_util/variant/87619.cc: Increase timeout for
	C++20 mode.
	* testsuite/20_util/variant/constexpr.cc: New test.
	* testsuite/20_util/variant/version.cc: New test.
diff --git a/libstdc++-v3/include/std/variant b/libstdc++-v3/include/std/variant
index f490941..d18365f 100644
--- a/libstdc++-v3/include/std/variant
+++ b/libstdc++-v3/include/std/variant
@@ -39,13 +39,14 @@
 #include <bits/exception_defines.h>
 #include <bits/functional_hash.h>
 #include <bits/invoke.h>
-#include <ext/aligned_buffer.h>
 #include <bits/parse_numbers.h>
 #include <bits/stl_iterator_base_types.h>
 #include <bits/stl_iterator_base_funcs.h>
 #include <bits/stl_construct.h>
 #include <bits/utility.h> // in_place_index_t
-#if __cplusplus > 201703L
+#if __cplusplus == 201703L
+# include <ext/aligned_buffer.h>
+#else
 # include <compare>
 #endif
 
@@ -71,7 +72,12 @@
 } // namespace __variant
 } // namespace __detail
 
-#define __cpp_lib_variant 202102L
+#if __cplusplus >= 202002L && __cpp_concepts
+// P2231R1 constexpr needs constexpr unions and constrained destructors.
+# define __cpp_lib_variant 202106L
+#else
+# define __cpp_lib_variant 202102L
+#endif
 
   template<typename... _Types> class tuple;
   template<typename... _Types> class variant;
@@ -146,6 +152,7 @@
     __do_visit(_Visitor&& __visitor, _Variants&&... __variants);
 
   template <typename... _Types, typename _Tp>
+    _GLIBCXX20_CONSTEXPR
     decltype(auto)
     __variant_cast(_Tp&& __rhs)
     {
@@ -224,8 +231,12 @@
     __as(const std::variant<_Types...>&& __v) noexcept
     { return std::move(__v); }
 
+  // For C++17:
   // _Uninitialized<T> is guaranteed to be a trivially destructible type,
   // even if T is not.
+  // For C++20:
+  // _Uninitialized<T> is trivially destructible iff T is, so _Variant_union
+  // needs a constrained non-trivial destructor.
   template<typename _Type, bool = std::is_trivially_destructible_v<_Type>>
     struct _Uninitialized;
 
@@ -256,6 +267,37 @@
   template<typename _Type>
     struct _Uninitialized<_Type, false>
     {
+#if __cpp_lib_variant >= 202106L
+      template<typename... _Args>
+	constexpr
+	_Uninitialized(in_place_index_t<0>, _Args&&... __args)
+	: _M_storage(std::forward<_Args>(__args)...)
+	{ }
+
+      constexpr ~_Uninitialized() { }
+
+      _Uninitialized(const _Uninitialized&) = default;
+      _Uninitialized(_Uninitialized&&) = default;
+      _Uninitialized& operator=(const _Uninitialized&) = default;
+      _Uninitialized& operator=(_Uninitialized&&) = default;
+
+      constexpr const _Type& _M_get() const & noexcept
+      { return _M_storage; }
+
+      constexpr _Type& _M_get() & noexcept
+      { return _M_storage; }
+
+      constexpr const _Type&& _M_get() const && noexcept
+      { return std::move(_M_storage); }
+
+      constexpr _Type&& _M_get() && noexcept
+      { return std::move(_M_storage); }
+
+      union {
+	char _M_nope;
+	_Type _M_storage;
+      };
+#else
       template<typename... _Args>
 	constexpr
 	_Uninitialized(in_place_index_t<0>, _Args&&... __args)
@@ -277,6 +319,7 @@
       { return std::move(*_M_storage._M_ptr()); }
 
       __gnu_cxx::__aligned_membuf<_Type> _M_storage;
+#endif
     };
 
   template<size_t _Np, typename _Union>
@@ -353,15 +396,31 @@
       constexpr _Variadic_union() : _M_rest() { }
 
       template<typename... _Args>
-	constexpr _Variadic_union(in_place_index_t<0>, _Args&&... __args)
+	constexpr
+	_Variadic_union(in_place_index_t<0>, _Args&&... __args)
 	: _M_first(in_place_index<0>, std::forward<_Args>(__args)...)
 	{ }
 
       template<size_t _Np, typename... _Args>
-	constexpr _Variadic_union(in_place_index_t<_Np>, _Args&&... __args)
+	constexpr
+	_Variadic_union(in_place_index_t<_Np>, _Args&&... __args)
 	: _M_rest(in_place_index<_Np-1>, std::forward<_Args>(__args)...)
 	{ }
 
+#if __cpp_lib_variant >= 202106L
+      _Variadic_union(const _Variadic_union&) = default;
+      _Variadic_union(_Variadic_union&&) = default;
+      _Variadic_union& operator=(const _Variadic_union&) = default;
+      _Variadic_union& operator=(_Variadic_union&&) = default;
+
+      ~_Variadic_union() = default;
+
+      constexpr ~_Variadic_union()
+	requires (!__has_trivial_destructor(_First))
+	      || (!__has_trivial_destructor(_Variadic_union<_Rest...>))
+      { }
+#endif
+
       _Uninitialized<_First> _M_first;
       _Variadic_union<_Rest...> _M_rest;
     };
@@ -406,6 +465,10 @@
   template<typename... _Types>
     struct _Variant_storage<false, _Types...>
     {
+      template<typename _Tp>
+	static constexpr size_t __index_of
+	  = __detail::__variant::__index_of_v<_Tp, _Types...>;
+
       constexpr
       _Variant_storage()
       : _M_index(static_cast<__index_type>(variant_npos))
@@ -418,7 +481,8 @@
 	_M_index{_Np}
 	{ }
 
-      void _M_reset()
+      constexpr void
+      _M_reset()
       {
 	if (!_M_valid()) [[unlikely]]
 	  return;
@@ -431,6 +495,7 @@
 	_M_index = static_cast<__index_type>(variant_npos);
       }
 
+      _GLIBCXX20_CONSTEXPR
       ~_Variant_storage()
       { _M_reset(); }
 
@@ -450,6 +515,10 @@
   template<typename... _Types>
     struct _Variant_storage<true, _Types...>
     {
+      template<typename _Tp>
+	static constexpr size_t __index_of
+	  = __detail::__variant::__index_of_v<_Tp, _Types...>;
+
       constexpr
       _Variant_storage()
       : _M_index(static_cast<__index_type>(variant_npos))
@@ -462,7 +531,8 @@
 	_M_index{_Np}
 	{ }
 
-      void _M_reset() noexcept
+      constexpr void
+      _M_reset() noexcept
       { _M_index = static_cast<__index_type>(variant_npos); }
 
       constexpr bool
@@ -489,17 +559,25 @@
 	_Variant_storage<_Traits<_Types...>::_S_trivial_dtor, _Types...>;
 
   template<typename _Tp, typename _Up>
-    void __variant_construct_single(_Tp&& __lhs, _Up&& __rhs_mem)
+    _GLIBCXX20_CONSTEXPR
+    void
+    __variant_construct_single(_Tp&& __lhs, _Up&& __rhs_mem)
     {
-      void* __storage = std::addressof(__lhs._M_u);
-      using _Type = remove_reference_t<decltype(__rhs_mem)>;
+      using _Type = __remove_cvref_t<_Up>;
+
       if constexpr (!is_same_v<_Type, __variant_cookie>)
-        ::new (__storage)
-	  _Type(std::forward<decltype(__rhs_mem)>(__rhs_mem));
+	{
+	  using _Lhs = remove_reference_t<_Tp>;
+	  std::_Construct(std::__addressof(__lhs._M_u),
+			  in_place_index<_Lhs::template __index_of<_Type>>,
+			  std::forward<_Up>(__rhs_mem));
+	}
     }
 
   template<typename... _Types, typename _Tp, typename _Up>
-    void __variant_construct(_Tp&& __lhs, _Up&& __rhs)
+    _GLIBCXX20_CONSTEXPR
+    void
+    __variant_construct(_Tp&& __lhs, _Up&& __rhs)
     {
       __lhs._M_index = __rhs._M_index;
       __variant::__raw_visit([&__lhs](auto&& __rhs_mem) mutable
@@ -518,6 +596,7 @@
       using _Base = _Variant_storage_alias<_Types...>;
       using _Base::_Base;
 
+      _GLIBCXX20_CONSTEXPR
       _Copy_ctor_base(const _Copy_ctor_base& __rhs)
 	  noexcept(_Traits<_Types...>::_S_nothrow_copy_ctor)
       {
@@ -546,6 +625,7 @@
       using _Base = _Copy_ctor_alias<_Types...>;
       using _Base::_Base;
 
+      _GLIBCXX20_CONSTEXPR
       _Move_ctor_base(_Move_ctor_base&& __rhs)
 	  noexcept(_Traits<_Types...>::_S_nothrow_move_ctor)
       {
@@ -553,6 +633,7 @@
       }
 
       template<typename _Up>
+	_GLIBCXX20_CONSTEXPR
         void _M_destructive_move(unsigned short __rhs_index, _Up&& __rhs)
         {
 	  this->_M_reset();
@@ -561,6 +642,7 @@
 	}
 
       template<typename _Up>
+	_GLIBCXX20_CONSTEXPR
         void _M_destructive_copy(unsigned short __rhs_index, const _Up& __rhs)
         {
 	  this->_M_reset();
@@ -580,6 +662,7 @@
       using _Base::_Base;
 
       template<typename _Up>
+	_GLIBCXX20_CONSTEXPR
         void _M_destructive_move(unsigned short __rhs_index, _Up&& __rhs)
         {
 	  this->_M_reset();
@@ -588,6 +671,7 @@
 	}
 
       template<typename _Up>
+	_GLIBCXX20_CONSTEXPR
         void _M_destructive_copy(unsigned short __rhs_index, const _Up& __rhs)
         {
 	  this->_M_reset();
@@ -606,6 +690,7 @@
       using _Base = _Move_ctor_alias<_Types...>;
       using _Base::_Base;
 
+      _GLIBCXX20_CONSTEXPR
       _Copy_assign_base&
       operator=(const _Copy_assign_base& __rhs)
 	  noexcept(_Traits<_Types...>::_S_nothrow_copy_assign)
@@ -664,6 +749,7 @@
       using _Base = _Copy_assign_alias<_Types...>;
       using _Base::_Base;
 
+      _GLIBCXX20_CONSTEXPR
       _Move_assign_base&
       operator=(_Move_assign_base&& __rhs)
 	  noexcept(_Traits<_Types...>::_S_nothrow_move_assign)
@@ -707,8 +793,7 @@
       using _Base = _Move_assign_alias<_Types...>;
 
       constexpr
-      _Variant_base()
-	  noexcept(_Traits<_Types...>::_S_nothrow_default_ctor)
+      _Variant_base() noexcept(_Traits<_Types...>::_S_nothrow_default_ctor)
       : _Variant_base(in_place_index<0>) { }
 
       template<size_t _Np, typename... _Args>
@@ -1095,13 +1180,12 @@
     }
 
   template<size_t _Np, typename _Variant, typename... _Args>
+    _GLIBCXX20_CONSTEXPR
     inline void
     __construct_by_index(_Variant& __v, _Args&&... __args)
     {
-      auto&& __storage = __detail::__variant::__get<_Np>(__v);
-      ::new ((void*)std::addressof(__storage))
-        remove_reference_t<decltype(__storage)>
-	  (std::forward<_Args>(__args)...);
+      std::_Construct(std::__addressof(__variant::__get<_Np>(__v)),
+		      std::forward<_Args>(__args)...);
       // Construction didn't throw, so can set the new index now:
       __v._M_index = _Np;
     }
@@ -1285,6 +1369,7 @@
     visit(_Visitor&&, _Variants&&...);
 
   template<typename... _Types>
+    _GLIBCXX20_CONSTEXPR
     inline enable_if_t<(is_move_constructible_v<_Types> && ...)
 			&& (is_swappable_v<_Types> && ...)>
     swap(variant<_Types...>& __lhs, variant<_Types...>& __rhs)
@@ -1342,9 +1427,11 @@
     {
     private:
       template <typename... _UTypes, typename _Tp>
-	friend decltype(auto) __variant_cast(_Tp&&);
+	friend _GLIBCXX20_CONSTEXPR decltype(auto)
+	__variant_cast(_Tp&&);
+
       template<size_t _Np, typename _Variant, typename... _Args>
-	friend void
+	friend _GLIBCXX20_CONSTEXPR void
 	__detail::__variant::__construct_by_index(_Variant& __v,
 						  _Args&&... __args);
 
@@ -1402,7 +1489,7 @@
       variant(variant&&) = default;
       variant& operator=(const variant&) = default;
       variant& operator=(variant&&) = default;
-      ~variant() = default;
+      _GLIBCXX20_CONSTEXPR ~variant() = default;
 
       template<typename _Tp,
 	       typename = enable_if_t<sizeof...(_Types) != 0>,
@@ -1459,6 +1546,7 @@
 	{ }
 
       template<typename _Tp>
+	_GLIBCXX20_CONSTEXPR
 	enable_if_t<__exactly_once<__accepted_type<_Tp&&>>
 		    && is_constructible_v<__accepted_type<_Tp&&>, _Tp>
 		    && is_assignable_v<__accepted_type<_Tp&&>&, _Tp>,
@@ -1483,6 +1571,7 @@
 	}
 
       template<typename _Tp, typename... _Args>
+	_GLIBCXX20_CONSTEXPR
 	enable_if_t<is_constructible_v<_Tp, _Args...> && __exactly_once<_Tp>,
 		    _Tp&>
 	emplace(_Args&&... __args)
@@ -1492,6 +1581,7 @@
 	}
 
       template<typename _Tp, typename _Up, typename... _Args>
+	_GLIBCXX20_CONSTEXPR
 	enable_if_t<is_constructible_v<_Tp, initializer_list<_Up>&, _Args...>
 		    && __exactly_once<_Tp>,
 		    _Tp&>
@@ -1502,6 +1592,7 @@
 	}
 
       template<size_t _Np, typename... _Args>
+	_GLIBCXX20_CONSTEXPR
 	enable_if_t<is_constructible_v<variant_alternative_t<_Np, variant>,
 				       _Args...>,
 		    variant_alternative_t<_Np, variant>&>
@@ -1548,6 +1639,7 @@
 	}
 
       template<size_t _Np, typename _Up, typename... _Args>
+	_GLIBCXX20_CONSTEXPR
 	enable_if_t<is_constructible_v<variant_alternative_t<_Np, variant>,
 				       initializer_list<_Up>&, _Args...>,
 		    variant_alternative_t<_Np, variant>&>
@@ -1601,6 +1693,7 @@
 	  return size_t(__index_type(this->_M_index + 1)) - 1;
       }
 
+      _GLIBCXX20_CONSTEXPR
       void
       swap(variant& __rhs)
       noexcept((__is_nothrow_swappable<_Types>::value && ...)
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index a395c05..0d7ae3b 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -173,7 +173,9 @@
 # define __cpp_lib_to_chars 201611L
 #endif
 #define __cpp_lib_unordered_map_try_emplace 201411
-#define __cpp_lib_variant 202102L
+#if __cplusplus == 201703L || ! __cpp_concepts // N.B. updated value in C++20
+# define __cpp_lib_variant 202102L
+#endif
 #endif
 
 #if __cplusplus >= 202002L
@@ -280,6 +282,9 @@
 # endif
 #define __cpp_lib_to_address 201711L
 #define __cpp_lib_to_array 201907L
+#if __cpp_concepts
+# define __cpp_lib_variant 202106L
+#endif
 #endif
 
 #if __cplusplus > 202002L
diff --git a/libstdc++-v3/testsuite/20_util/optional/version.cc b/libstdc++-v3/testsuite/20_util/optional/version.cc
index d8c9851..c18ecb8 100644
--- a/libstdc++-v3/testsuite/20_util/optional/version.cc
+++ b/libstdc++-v3/testsuite/20_util/optional/version.cc
@@ -4,8 +4,8 @@
 
 #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 == 201703L && __cpp_lib_optional != 201606L
+# error "Feature test macro for optional has wrong value for C++17 in <version>"
 #elif __cplusplus >= 202002L && __cpp_lib_optional < 202106L
 # error "Feature test macro for optional has wrong value for C++20 in <version>"
 #endif
diff --git a/libstdc++-v3/testsuite/20_util/variant/87619.cc b/libstdc++-v3/testsuite/20_util/variant/87619.cc
index e83fa03..c878510 100644
--- a/libstdc++-v3/testsuite/20_util/variant/87619.cc
+++ b/libstdc++-v3/testsuite/20_util/variant/87619.cc
@@ -16,6 +16,8 @@
 // <http://www.gnu.org/licenses/>.
 
 // { dg-do compile { target c++17 } }
+// FIXME: Need increased timeout due to PR c++/102780
+// { dg-timeout-factor 2 { target c++20 } }
 
 #include <variant>
 #include <utility>
diff --git a/libstdc++-v3/testsuite/20_util/variant/constexpr.cc b/libstdc++-v3/testsuite/20_util/variant/constexpr.cc
new file mode 100644
index 0000000..7af3d98
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/variant/constexpr.cc
@@ -0,0 +1,138 @@
+// { dg-options "-std=gnu++20" }
+// { dg-do compile { target c++20 } }
+
+#include <variant>
+
+// P2231R1 Missing constexpr in std::optional and std::variant
+
+#ifndef __cpp_lib_variant
+#error "Feature test macro for variant is missing in <variant>"
+#elif __cpp_lib_variant < 202106L
+# error "Feature test macro for variant has wrong value for C++20 in <variant>"
+#endif
+
+#include <testsuite_hooks.h>
+
+
+constexpr bool
+test_assign()
+{
+  std::variant<int, double> v1(1);
+  v1 = 2.5;
+  VERIFY( std::get<double>(v1) == 2.5 );
+
+  v1 = 99;
+  VERIFY( std::get<int>(v1) == 99 );
+  v1 = 999;
+  VERIFY( std::get<int>(v1) == 999 );
+
+  struct S // non-trivial special members
+  {
+    constexpr S(int i) : i(i) { }
+    constexpr ~S() { }
+    constexpr S(const S& s) : i(s.i) { }
+
+    int i;
+  };
+
+  std::variant<int, S> v;
+  v = S(123);
+  VERIFY( std::get<1>(v).i == 123 );
+
+  const S s(456);
+  v = s;
+  VERIFY( std::get<1>(v).i == 456 );
+
+  v = 789;
+  VERIFY( std::get<0>(v) == 789 );
+
+  return true;
+}
+
+static_assert( test_assign() );
+
+constexpr bool
+test_emplace()
+{
+  struct S // non-trivial special members
+  {
+    constexpr S(std::initializer_list<int> l) : i(l.begin()[0]) { }
+    constexpr S(std::initializer_list<int> l, int n) : i(l.begin()[n]) { }
+    constexpr ~S() { }
+    constexpr S(const S& s) : i(s.i) { }
+
+    int i;
+  };
+
+  std::variant<int, double, S> v(1);
+
+  // template<class T, class... Args> constexpr T& emplace(Args&&... args);
+  v.emplace<double>(2.0);
+  VERIFY( std::get<1>(v) == 2.0 );
+  v.emplace<double>(2.5);
+  VERIFY( std::get<1>(v) == 2.5 );
+  v.emplace<int>(2.5);
+  VERIFY( std::get<0>(v) == 2 );
+
+  // template<class T, class U, class... Args>
+  // constexpr T& emplace(initializer_list<U>, Args&&... args);
+  v.emplace<S>({3, 2, 1});
+  VERIFY( std::get<2>(v).i == 3 );
+  v.emplace<S>({3, 2, 1}, 1);
+  VERIFY( std::get<2>(v).i == 2 );
+
+  // template<size_t I, class... Args>
+  // constexpr variant_alternative_t<I, ...>& emplace(Args&&... args);
+  v.emplace<1>(3.0);
+  VERIFY( std::get<1>(v) == 3.0 );
+  v.emplace<1>(0.5);
+  VERIFY( std::get<1>(v) == 0.5 );
+  v.emplace<0>(1.5);
+  VERIFY( std::get<0>(v) == 1 );
+
+  // template<size_t I, class U, class... Args>
+  // constexpr variant_alternative_t<I, ...>&
+  // emplace(initializer_list<U>, Args&&... args);
+  v.emplace<2>({7, 8, 9});
+  VERIFY( std::get<2>(v).i == 7 );
+  v.emplace<2>({13, 12, 11}, 1);
+  VERIFY( std::get<2>(v).i == 12 );
+
+  return true;
+}
+
+static_assert( test_emplace() );
+
+constexpr bool
+test_swap()
+{
+  std::variant<int, double> v1(1), v2(2.5);
+  v1.swap(v2);
+  VERIFY( std::get<double>(v1) == 2.5 );
+  VERIFY( std::get<int>(v2) == 1 );
+
+  swap(v1, v2);
+  VERIFY( std::get<int>(v1) == 1 );
+  VERIFY( std::get<double>(v2) == 2.5 );
+
+  struct S
+  {
+    constexpr S(int i) : i(i) { }
+    constexpr S(S&& s) : i(s.i) { }
+    constexpr S& operator=(S&& s) { i = s.i; s.i = -1; return *this; }
+
+    int i;
+  };
+
+  std::variant<int, S> v3(3), v4(S(4));
+  v3.swap(v4);
+  VERIFY( std::get<S>(v3).i == 4 );
+  VERIFY( std::get<int>(v4) == 3 );
+  v3.swap(v4);
+  VERIFY( std::get<int>(v3) == 3 );
+  VERIFY( std::get<S>(v4).i == 4 );
+
+  return true;
+}
+
+static_assert( test_swap() );
diff --git a/libstdc++-v3/testsuite/20_util/variant/version.cc b/libstdc++-v3/testsuite/20_util/variant/version.cc
new file mode 100644
index 0000000..de04c5e
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/variant/version.cc
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++17 } }
+
+#include <version>
+
+#ifndef __cpp_lib_variant
+#error "Feature test macro for variant is missing in <version>"
+#elif __cplusplus == 201703L && __cpp_lib_variant != 202102L
+# error "Feature test macro for variant has wrong value for C++17 in <version>"
+#elif __cplusplus >= 202002L && __cpp_lib_variant < 202106L
+# error "Feature test macro for variant has wrong value for C++20 in <version>"
+#endif