+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
struct __is_fast_hash<hash<u32string>> : std::false_type
{ };
-#if __cplusplus > 201103L
+#if __cplusplus >= 201402L
#define __cpp_lib_string_udls 201304
} // 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
// @} 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
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
{ __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
// @} 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
_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
/// @}
+ 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
_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>
{ 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>
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);
}
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);
}
main()
{
test01();
+ test02();
}
--- /dev/null
+// 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();
+}
#include <string>
#include <vector>
#include <unordered_set>
+#include <memory_resource>
#include <testsuite_hooks.h>
using namespace std;
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);
{
struct A
{
- operator string()
+ operator pmr::string()
{
throw nullptr;
}
};
- variant<int, string> v;
+ variant<int, pmr::string> v;
try
{
v.emplace<1>(A{});