PR libstdc++/87431 re-adjust never-valueless optimizations
authorJonathan Wakely <jwakely@redhat.com>
Fri, 5 Apr 2019 16:56:09 +0000 (17:56 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 5 Apr 2019 16:56:09 +0000 (17:56 +0100)
Avoid creating arbitrarily large objects on the stack when emplacing
trivially copyable objects into a variant. Currently we provide the
strong exception-safety guarantee for all trivially copyable types, by
constructing a second variant and then doing a non-throwing move
assignment from the temporary. This patch restricts that behaviour to
trivially copyable types that are no larger than 256 bytes. For larger
types the object will be emplaced directly into the variant, and if its
initialization throws then the variant becomes valueless.

Also implement Antony Polukhin's suggestion to whitelist specific types
that are not trivially copyable but can be efficiently move-assigned.
Emplacing those types will never cause a variant to become valueless.
The whitelisted types are: std::shared_ptr, std::weak_ptr,
std::unique_ptr, std::function, and std::any. Additionally,
std::basic_string, std::vector, and __gnu_debug::vector are whitelisted
if their allocator traits give them a non-throwing move assignment
operator. Specifically, this means std::string is whitelisted, but
std::pmr::string is not.

As part of this patch, additional if-constexpr branches are added for
the cases where the initialization is known to be non-throwing (so the
overhead of the try-catch block can be avoided) and where a scalar is
being produced by a potentially-throwing conversion operator (so that
the overhead of constructing and move-assigning a variant is avoided).
These changes should have no semantic effect, just better codegen.

PR libstdc++/87431 (again)
* include/bits/basic_string.h (__variant::_Never_valueless_alt):
Define partial specialization for basic_string.
* include/bits/shared_ptr.h (_Never_valueless_alt): Likewise for
shared_ptr and weak_ptr.
* include/bits/std_function.h (_Never_valueless_alt): Likewise for
function.
* include/bits/stl_vector.h (_Never_valueless_alt): Likewise for
vector.
* include/bits/unique_ptr.h (_Never_valueless_alt): Likewise for
unique_ptr.
* include/debug/vector (_Never_valueless_alt): Likewise for debug
vector.
* include/std/any (_Never_valueless_alt): Define explicit
specialization for any.
* include/std/variant (_Never_valueless_alt): Define primary template.
(__never_valueless): Use _Never_valueless_alt instead of
is_trivially_copyable.
(variant::emplace<N>(Args&&...)): Add special case for non-throwing
initializations to avoid try-catch overhead. Add special case for
scalars produced by potentially-throwing conversions. Use
_Never_valueless_alt instead of is_trivially_copyable for the
remaining strong exception-safety cases.
(variant::emplace<N>(initializer_list<U>, Args&&...)): Likewise.
* testsuite/20_util/variant/87431.cc: Run both test functions.
* testsuite/20_util/variant/exception_safety.cc: New test.
* testsuite/20_util/variant/run.cc: Use pmr::string instead of string,
so the variant becomes valueless.

From-SVN: r270170

12 files changed:
libstdc++-v3/ChangeLog
libstdc++-v3/include/bits/basic_string.h
libstdc++-v3/include/bits/shared_ptr.h
libstdc++-v3/include/bits/std_function.h
libstdc++-v3/include/bits/stl_vector.h
libstdc++-v3/include/bits/unique_ptr.h
libstdc++-v3/include/debug/vector
libstdc++-v3/include/std/any
libstdc++-v3/include/std/variant
libstdc++-v3/testsuite/20_util/variant/87431.cc
libstdc++-v3/testsuite/20_util/variant/exception_safety.cc [new file with mode: 0644]
libstdc++-v3/testsuite/20_util/variant/run.cc

index e20388752203c9f7d9e3dcd30fcf4c1b4b51b162..a34428f762b8264cb469e5db88b38067ae37b73e 100644 (file)
@@ -1,3 +1,34 @@
+2019-04-05  Jonathan Wakely  <jwakely@redhat.com>
+
+       PR libstdc++/87431 (again)
+       * include/bits/basic_string.h (__variant::_Never_valueless_alt):
+       Define partial specialization for basic_string.
+       * include/bits/shared_ptr.h (_Never_valueless_alt): Likewise for
+       shared_ptr and weak_ptr.
+       * include/bits/std_function.h (_Never_valueless_alt): Likewise for
+       function.
+       * include/bits/stl_vector.h (_Never_valueless_alt): Likewise for
+       vector.
+       * include/bits/unique_ptr.h (_Never_valueless_alt): Likewise for
+       unique_ptr.
+       * include/debug/vector (_Never_valueless_alt): Likewise for debug
+       vector.
+       * include/std/any (_Never_valueless_alt): Define explicit
+       specialization for any.
+       * include/std/variant (_Never_valueless_alt): Define primary template.
+       (__never_valueless): Use _Never_valueless_alt instead of
+       is_trivially_copyable.
+       (variant::emplace<N>(Args&&...)): Add special case for non-throwing
+       initializations to avoid try-catch overhead. Add special case for
+       scalars produced by potentially-throwing conversions. Use
+       _Never_valueless_alt instead of is_trivially_copyable for the
+       remaining strong exception-safety cases.
+       (variant::emplace<N>(initializer_list<U>, Args&&...)): Likewise.
+       * testsuite/20_util/variant/87431.cc: Run both test functions.
+       * testsuite/20_util/variant/exception_safety.cc: New test.
+       * testsuite/20_util/variant/run.cc: Use pmr::string instead of string,
+       so the variant becomes valueless.
+
 2019-04-03  Jonathan Wakely  <jwakely@redhat.com>
 
        PR libstdc++/85184
index 4d0894be99b1340a85c885ff30c71f9f397c2533..20a56277a57dc6f3356b6cd8ecab3f6144c4535f 100644 (file)
@@ -6800,7 +6800,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct __is_fast_hash<hash<u32string>> : std::false_type
     { };
 
-#if __cplusplus > 201103L
+#if __cplusplus >= 201402L
 
 #define __cpp_lib_string_udls 201304
 
@@ -6843,7 +6843,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   } // inline namespace string_literals
   } // inline namespace literals
 
-#endif // __cplusplus > 201103L
+#if __cplusplus >= 201703L
+  namespace __detail::__variant
+  {
+    template<typename> struct _Never_valueless_alt; // see <variant>
+
+    // Provide the strong exception-safety guarantee when emplacing a
+    // basic_string into a variant, but only if move assignment cannot throw.
+    template<typename _Tp, typename _Traits, typename _Alloc>
+      struct _Never_valueless_alt<std::basic_string<_Tp, _Traits, _Alloc>>
+      : std::is_nothrow_move_assignable<std::basic_string<_Tp, _Traits, _Alloc>>
+      { };
+  }  // namespace __detail::__variant
+#endif // C++17
+#endif // C++14
 
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
index ee815f0d0a197b1d72ab78b333f388f6a909db8d..2d53478f1f4fd04de3e161fb2c4b7d98fde06646 100644 (file)
@@ -732,6 +732,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // @} group pointer_abstractions
 
+#if __cplusplus >= 201703L
+  namespace __detail::__variant
+  {
+    template<typename> struct _Never_valueless_alt; // see <variant>
+
+    // Provide the strong exception-safety guarantee when emplacing a
+    // shared_ptr into a variant.
+    template<typename _Tp>
+      struct _Never_valueless_alt<std::shared_ptr<_Tp>>
+      : std::true_type
+      { };
+
+    // Provide the strong exception-safety guarantee when emplacing a
+    // weak_ptr into a variant.
+    template<typename _Tp>
+      struct _Never_valueless_alt<std::weak_ptr<_Tp>>
+      : std::true_type
+      { };
+  }  // namespace __detail::__variant
+#endif // C++17
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
index caa0fd816af1e2c8cad6695eba1f7bc17b5820a5..b70ed564d11a2bda4f34bcc23c3340c1fa7db17f 100644 (file)
@@ -787,9 +787,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     swap(function<_Res(_Args...)>& __x, function<_Res(_Args...)>& __y) noexcept
     { __x.swap(__y); }
 
+#if __cplusplus >= 201703L
+  namespace __detail::__variant
+  {
+    template<typename> struct _Never_valueless_alt; // see <variant>
+
+    // Provide the strong exception-safety guarantee when emplacing a
+    // function into a variant.
+    template<typename _Signature>
+      struct _Never_valueless_alt<std::function<_Signature>>
+      : std::true_type
+      { };
+  }  // namespace __detail::__variant
+#endif // C++17
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
 #endif // C++11
-
 #endif // _GLIBCXX_STD_FUNCTION_H
index 10bf4fac62ed6e5c23fb9c2663f372082600bfe5..dd9382d254d54c25a85901579d9173d455ecd580 100644 (file)
@@ -1938,6 +1938,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     { __x.swap(__y); }
 
 _GLIBCXX_END_NAMESPACE_CONTAINER
+
+#if __cplusplus >= 201703L
+  namespace __detail::__variant
+  {
+    template<typename> struct _Never_valueless_alt; // see <variant>
+
+    // Provide the strong exception-safety guarantee when emplacing a
+    // vector into a variant, but only if move assignment cannot throw.
+    template<typename _Tp, typename _Alloc>
+      struct _Never_valueless_alt<_GLIBCXX_STD_C::vector<_Tp, _Alloc>>
+      : std::is_nothrow_move_assignable<_GLIBCXX_STD_C::vector<_Tp, _Alloc>>
+      { };
+  }  // namespace __detail::__variant
+#endif // C++17
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
index 61a3ef05460945cda4e56a8d7c1a6a1bcc20bbd6..963e4eb7d4402b54bf186e7f7f220eaaa648c3bc 100644 (file)
@@ -866,6 +866,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // @} group pointer_abstractions
 
+#if __cplusplus >= 201703L
+  namespace __detail::__variant
+  {
+    template<typename> struct _Never_valueless_alt; // see <variant>
+
+    // Provide the strong exception-safety guarantee when emplacing a
+    // unique_ptr into a variant.
+    template<typename _Tp, typename _Del>
+      struct _Never_valueless_alt<std::unique_ptr<_Tp, _Del>>
+      : std::true_type
+      { };
+  }  // namespace __detail::__variant
+#endif // C++17
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace
 
index 6d9536ee73abf8b10653ddd0d3d58b9afdee12ff..56b5e140fd348028cc54e74f0e8af8e030342c4e 100644 (file)
@@ -801,6 +801,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                 _Sequence, std::random_access_iterator_tag>& __it)
     { return std::__niter_base(__it.base()); }
 
+#if __cplusplus >= 201703L
+  namespace __detail::__variant
+  {
+    template<typename> struct _Never_valueless_alt; // see <variant>
+
+    // Provide the strong exception-safety guarantee when emplacing a
+    // vector into a variant, but only if move assignment cannot throw.
+    template<typename _Tp, typename _Alloc>
+      struct _Never_valueless_alt<__debug::vector<_Tp, _Alloc>>
+      : std::is_nothrow_move_assignable<__debug::vector<_Tp, _Alloc>>
+      { };
+  }  // namespace __detail::__variant
+#endif // C++17
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
index 385a99ce7441e28c645122610a40dc0e446a2945..b0553dccf22448025603acabd7c34d5a113b4de5 100644 (file)
@@ -614,9 +614,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// @}
 
+  namespace __detail::__variant
+  {
+    template<typename> struct _Never_valueless_alt; // see <variant>
+
+    // Provide the strong exception-safety guarantee when emplacing an
+    // any into a variant.
+    template<>
+      struct _Never_valueless_alt<std::any>
+      : std::true_type
+      { };
+  }  // namespace __detail::__variant
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
-#endif // C++14
-
+#endif // C++17
 #endif // _GLIBCXX_ANY
index a21ef3005cfd5e10b4e5d1969b6509ce0c7136ff..e52aa403009ea361698e57fc4a2028b5a3fb42b4 100644 (file)
@@ -327,11 +327,31 @@ namespace __variant
       _Variadic_union<_Rest...> _M_rest;
     };
 
+  // _Never_valueless_alt is true for variant alternatives that can
+  // always be placed in a variant without it becoming valueless.
+
+  // For suitably-small, trivially copyable types we can create temporaries
+  // on the stack and then memcpy them into place.
+  template<typename _Tp>
+    struct _Never_valueless_alt
+    : __and_<bool_constant<sizeof(_Tp) <= 256>, is_trivially_copyable<_Tp>>
+    { };
+
+  // Specialize _Never_valueless_alt for other types which have a
+  // non-throwing and cheap move assignment operator, so that emplacing
+  // the type will provide the strong exception-safety guarantee,
+  // by creating and moving a temporary.
+  // Whether _Never_valueless_alt<T> is true or not affects the ABI of a
+  // variant using that alternative, so we can't change the value later!
+
+  // True if every alternative in _Types... can be emplaced in a variant
+  // without it becoming valueless. If this is true, variant<_Types...>
+  // can never be valueless, which enables some minor optimizations.
   template <typename... _Types>
-  constexpr bool __never_valueless()
-  {
-    return (is_trivially_copyable_v<_Types> && ...);
-  }
+    constexpr bool __never_valueless()
+    {
+      return (_Never_valueless_alt<_Types>::value && ...);
+    }
 
   // Defines index and the dtor, possibly trivial.
   template<bool __trivially_destructible, typename... _Types>
@@ -776,19 +796,19 @@ namespace __variant
     { return __v._M_storage(); }
 
   template <typename _Maybe_variant_cookie, typename _Variant>
-  struct _Extra_visit_slot_needed
-  {
-    template <typename> struct _Variant_never_valueless;
+    struct _Extra_visit_slot_needed
+    {
+      template <typename> struct _Variant_never_valueless;
 
-    template <typename... _Types>
-    struct _Variant_never_valueless<variant<_Types...>>
-      : bool_constant<__never_valueless<_Types...>()> {};
+      template <typename... _Types>
+       struct _Variant_never_valueless<variant<_Types...>>
+       : bool_constant<__never_valueless<_Types...>()> {};
 
-    static constexpr bool value =
-      (is_same_v<_Maybe_variant_cookie, __variant_cookie>
-       || is_same_v<_Maybe_variant_cookie, __variant_idx_cookie>)
-      && !_Variant_never_valueless<__remove_cvref_t<_Variant>>::value;
-  };
+      static constexpr bool value =
+       (is_same_v<_Maybe_variant_cookie, __variant_cookie>
+        || is_same_v<_Maybe_variant_cookie, __variant_idx_cookie>)
+       && !_Variant_never_valueless<__remove_cvref_t<_Variant>>::value;
+    };
 
   // Used for storing multi-dimensional vtable.
   template<typename _Tp, size_t... _Dimensions>
@@ -1329,31 +1349,45 @@ namespace __variant
          static_assert(_Np < sizeof...(_Types),
                        "The index should be in [0, number of alternatives)");
          using type = variant_alternative_t<_Np, variant>;
-         // If constructing the value can throw but move assigning it can't,
-         // construct in a temporary and then move assign from it. This gives
-         // the strong exception safety guarantee, ensuring we never become
-         // valueless.
-         if constexpr (is_trivially_copyable_v<type>
-             && !is_nothrow_constructible_v<type, _Args...>)
+         // Provide the strong exception-safety guarantee when possible,
+         // to avoid becoming valueless.
+         if constexpr (is_nothrow_constructible_v<type, _Args...>)
+           {
+             this->_M_reset();
+             __variant_construct_by_index<_Np>(*this,
+                 std::forward<_Args>(__args)...);
+           }
+         else if constexpr (is_scalar_v<type>)
+           {
+             // This might invoke a potentially-throwing conversion operator:
+             const type __tmp(std::forward<_Args>(__args)...);
+             // But these steps won't throw:
+             this->_M_reset();
+             __variant_construct_by_index<_Np>(*this, __tmp);
+           }
+         else if constexpr (__detail::__variant::_Never_valueless_alt<type>())
            {
-             // If move assignment cannot throw then we can provide the
-             // strong exception safety guarantee, and never become valueless.
+             // This construction might throw:
              variant __tmp(in_place_index<_Np>,
                            std::forward<_Args>(__args)...);
+             // But _Never_valueless_alt<type> means this won't:
              *this = std::move(__tmp);
-             return std::get<_Np>(*this);
            }
-
-         this->_M_reset();
-         __try
-           {
-             __variant_construct_by_index<_Np>(*this,
-               std::forward<_Args>(__args)...);
-           }
-         __catch (...)
+         else
            {
-             this->_M_index = variant_npos;
-             __throw_exception_again;
+             // This case only provides the basic exception-safety guarantee,
+             // i.e. the variant can become valueless.
+             this->_M_reset();
+             __try
+               {
+                 __variant_construct_by_index<_Np>(*this,
+                   std::forward<_Args>(__args)...);
+               }
+             __catch (...)
+               {
+                 this->_M_index = variant_npos;
+                 __throw_exception_again;
+               }
            }
          return std::get<_Np>(*this);
        }
@@ -1367,28 +1401,39 @@ namespace __variant
          static_assert(_Np < sizeof...(_Types),
                        "The index should be in [0, number of alternatives)");
          using type = variant_alternative_t<_Np, variant>;
-         if constexpr (is_trivially_copyable_v<type>
-             && !is_nothrow_constructible_v<type, initializer_list<_Up>,
-                                            _Args...>)
+         // Provide the strong exception-safety guarantee when possible,
+         // to avoid becoming valueless.
+         if constexpr (is_nothrow_constructible_v<type,
+                                                  initializer_list<_Up>&,
+                                                  _Args...>)
            {
-             // If move assignment cannot throw then we can provide the
-             // strong exception safety guarantee, and never become valueless.
+             this->_M_reset();
+             __variant_construct_by_index<_Np>(*this, __il,
+                 std::forward<_Args>(__args)...);
+           }
+         else if constexpr (__detail::__variant::_Never_valueless_alt<type>())
+           {
+             // This construction might throw:
              variant __tmp(in_place_index<_Np>, __il,
                            std::forward<_Args>(__args)...);
+             // But _Never_valueless_alt<type> means this won't:
              *this = std::move(__tmp);
-             return std::get<_Np>(*this);
            }
-
-         this->_M_reset();
-         __try
-           {
-             __variant_construct_by_index<_Np>(*this, __il,
-               std::forward<_Args>(__args)...);
-           }
-         __catch (...)
+         else
            {
-             this->_M_index = variant_npos;
-             __throw_exception_again;
+             // This case only provides the basic exception-safety guarantee,
+             // i.e. the variant can become valueless.
+             this->_M_reset();
+             __try
+               {
+                 __variant_construct_by_index<_Np>(*this, __il,
+                   std::forward<_Args>(__args)...);
+               }
+             __catch (...)
+               {
+                 this->_M_index = variant_npos;
+                 __throw_exception_again;
+               }
            }
          return std::get<_Np>(*this);
        }
index 8c5c4a7815407d85e5472765b9cd907c3c0e01e3..bf92564cf926ff3dd658cded5b9d5c30e1eaba3a 100644 (file)
@@ -68,4 +68,5 @@ int
 main()
 {
   test01();
+  test02();
 }
diff --git a/libstdc++-v3/testsuite/20_util/variant/exception_safety.cc b/libstdc++-v3/testsuite/20_util/variant/exception_safety.cc
new file mode 100644 (file)
index 0000000..7e1b0f3
--- /dev/null
@@ -0,0 +1,178 @@
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++17" }
+// { dg-do run { target c++17 } }
+
+#include <variant>
+#include <vector>
+#include <string>
+#include <memory_resource>
+#include <memory>
+#include <functional>
+#include <any>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+#if _GLIBCXX_USE_CXX11_ABI
+  std::variant<int, std::pmr::string, std::pmr::vector<int>> v(1);
+  VERIFY( v.index() == 0 );
+
+  try
+  {
+    std::pmr::string s = "how long is a piece of SSO string?";
+    v.emplace<1>(s, std::pmr::null_memory_resource());
+    VERIFY( false );
+  }
+  catch(const std::bad_alloc&)
+  {
+    VERIFY( v.valueless_by_exception() );
+  }
+
+  v.emplace<0>(2);
+  VERIFY( v.index() == 0 );
+
+  try
+  {
+    v.emplace<2>({1, 2, 3}, std::pmr::null_memory_resource());
+    VERIFY( false );
+  }
+  catch(const std::bad_alloc&)
+  {
+    VERIFY( v.valueless_by_exception() );
+  }
+#endif
+}
+
+void
+test02()
+{
+  struct X
+  {
+    X(int i) : i(1) { if (i > 2) throw 3; }
+    X(std::initializer_list<int> l) : i(2) { if (l.size() > 2) throw 3; }
+    int i;
+  };
+  static_assert( std::is_trivially_copyable_v<X> );
+
+  std::variant<std::monostate, int, X> v(111);
+  VERIFY( v.index() == 1 );
+
+  try
+  {
+    v.emplace<X>(3);
+    VERIFY( false );
+  }
+  catch(int)
+  {
+    VERIFY( !v.valueless_by_exception() );
+    VERIFY( v.index() == 1 );
+    VERIFY( std::get<int>(v) == 111 );
+  }
+
+  v.emplace<X>(1);
+  VERIFY( v.index() == 2 );
+  VERIFY( std::get<X>(v).i == 1 );
+
+  try
+  {
+    v.emplace<X>(3);
+    VERIFY( false );
+  }
+  catch(int)
+  {
+    VERIFY( !v.valueless_by_exception() );
+    VERIFY( v.index() == 2 );
+    VERIFY( std::get<X>(v).i == 1 );
+  }
+
+  try
+  {
+    v.emplace<X>({1, 2, 3});
+    VERIFY( false );
+  }
+  catch(int)
+  {
+    VERIFY( !v.valueless_by_exception() );
+    VERIFY( v.index() == 2 );
+    VERIFY( std::get<X>(v).i == 1 );
+  }
+}
+
+template<typename T, typename V>
+  bool bad_emplace(V& v)
+  {
+    struct X {
+      operator T() const { throw 1; }
+    };
+
+    const auto index = v.index();
+
+    try
+    {
+      if (std::is_same_v<T, std::any>)
+      {
+       // Need to test std::any differently, because emplace<std::any>(X{})
+       // would create a std::any with a contained X, instead of using
+       // X::operator any() to convert to std::any.
+       struct ThrowOnCopy {
+         ThrowOnCopy() { }
+         ThrowOnCopy(const ThrowOnCopy&) { throw 1; }
+       } t;
+       v.template emplace<std::any>(t);
+      }
+      else
+       v.template emplace<T>(X{});
+    }
+    catch (int)
+    {
+      return v.index() == index;
+    }
+    return false;
+  }
+
+void
+test03()
+{
+  struct TriviallyCopyable { int i = 0; };
+
+  std::variant<std::monostate, int, TriviallyCopyable, std::optional<int>,
+    std::string, std::vector<int>, std::function<void()>, std::any,
+    std::shared_ptr<int>, std::weak_ptr<int>, std::unique_ptr<int>> v(1);
+  VERIFY( v.index() == 1 );
+
+  VERIFY( bad_emplace<int>(v) );
+  VERIFY( bad_emplace<TriviallyCopyable>(v) );
+  VERIFY( bad_emplace<std::optional<int>>(v) );
+  VERIFY( bad_emplace<std::string>(v) );
+  VERIFY( bad_emplace<std::vector<int>>(v) );
+  VERIFY( bad_emplace<std::function<void()>>(v) );
+  VERIFY( bad_emplace<std::any>(v) );
+  VERIFY( bad_emplace<std::shared_ptr<int>>(v) );
+  VERIFY( bad_emplace<std::weak_ptr<int>>(v) );
+  VERIFY( bad_emplace<std::unique_ptr<int>>(v) );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
index 7ee9b08c2ce8b40423d19e57ab1a5251d3b393d7..3ca375d374e592e8f3dd6c32835ed627fe9c5f15 100644 (file)
@@ -22,6 +22,7 @@
 #include <string>
 #include <vector>
 #include <unordered_set>
+#include <memory_resource>
 #include <testsuite_hooks.h>
 
 using namespace std;
@@ -376,7 +377,7 @@ void test_visit()
 
 void test_hash()
 {
-  unordered_set<variant<int, string>> s;
+  unordered_set<variant<int, pmr::string>> s;
   VERIFY(s.emplace(3).second);
   VERIFY(s.emplace("asdf").second);
   VERIFY(s.emplace().second);
@@ -388,12 +389,12 @@ void test_hash()
   {
     struct A
     {
-      operator string()
+      operator pmr::string()
       {
         throw nullptr;
       }
     };
-    variant<int, string> v;
+    variant<int, pmr::string> v;
     try
       {
         v.emplace<1>(A{});