2019-01-06 Jonathan Wakely <jwakely@redhat.com>
+ PR libstdc++/87431
+ * include/std/variant (_Variant_storage<true, _Types...>::_M_valid):
+ Check is_trivially_copyable instead of is_scalar.
+ (variant::emplace<N, Args>(Args&&...)): If construction of the new
+ contained value can throw and its type is trivially copyable then
+ construct into a temporary variant and move from it, to provide the
+ strong exception safety guarantee.
+ (variant::emplace<N, U, Args>(initializer_list<U>, Args&&...)):
+ Likewise.
+ * testsuite/20_util/variant/87431.cc: New test.
+ * testsuite/20_util/variant/run.cc: Adjust test so that throwing
+ conversion causes valueless state.
+
PR libstdc++/88607
* testsuite/17_intro/headers/c++1998/charset.cc: New test.
* testsuite/17_intro/headers/c++2011/charset.cc: New test.
constexpr bool
_M_valid() const noexcept
{
- if constexpr ((is_scalar_v<_Types> && ...))
+ if constexpr ((is_trivially_copyable_v<_Types> && ...))
return true;
return this->_M_index != __index_type(variant_npos);
}
{
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...>)
+ {
+ // If move assignment cannot throw then we can provide the
+ // strong exception safety guarantee, and never become valueless.
+ variant __tmp(in_place_index<_Np>,
+ std::forward<_Args>(__args)...);
+ *this = std::move(__tmp);
+ return std::get<_Np>(*this);
+ }
+
this->~variant();
__try
{
{
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...>)
+ {
+ // If move assignment cannot throw then we can provide the
+ // strong exception safety guarantee, and never become valueless.
+ variant __tmp(in_place_index<_Np>, __il,
+ std::forward<_Args>(__args)...);
+ *this = std::move(__tmp);
+ return std::get<_Np>(*this);
+ }
+
this->~variant();
__try
{
--- /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 <testsuite_hooks.h>
+
+void
+test01()
+{
+ struct ThrowingConversion {
+ operator int() { throw 0; }
+ };
+
+ std::variant<float, char, int> v{'a'};
+ try
+ {
+ ThrowingConversion x;
+ v.emplace<2>(x);
+ VERIFY(false);
+ }
+ catch (int)
+ {
+ VERIFY( !v.valueless_by_exception() );
+ VERIFY( v.index() == 1 );
+ VERIFY( std::get<1>(v) == 'a' );
+ }
+}
+
+void
+test02()
+{
+ struct ThrowingConstructor {
+ ThrowingConstructor(std::initializer_list<int>, char) { throw 1; }
+ };
+
+ std::variant<float, char, ThrowingConstructor> v{'a'};
+ try
+ {
+ v.emplace<2>({1, 2, 3}, '4');
+ VERIFY(false);
+ }
+ catch (int)
+ {
+ VERIFY( !v.valueless_by_exception() );
+ VERIFY( v.index() == 1 );
+ VERIFY( std::get<1>(v) == 'a' );
+ }
+}
+
+int
+main()
+{
+ test01();
+}
{
struct A
{
- operator int()
+ operator string()
{
throw nullptr;
}
variant<int, string> v;
try
{
- v.emplace<0>(A{});
+ v.emplace<1>(A{});
}
catch (nullptr_t)
{