Commit ce9f305e by Jonathan Wakely Committed by Jonathan Wakely

Add __is_nothrow_convertible to fix std::is_nothrow_invocable_r

The definition of is_nothrow_invocable in terms of is_convertible and
is_nothrow_constructible is incorrect, because a type could have an
explicit constructor that means is_nothrow_constructible is true, but
implicit conversions could use a different constructor that is
potentially-throwing.

Fix it by adding a C++11 version of C++20's is_nothrow_convertible that
only considers implicit conversions.

	* include/std/type_traits (__is_nt_convertible_helper): Define it
	unconditionally, not only for C++20.
	(__is_nothrow_convertible): Define internal trait for use in C++11.
	(__is_nt_invocable_impl: Fix by using __is_nothrow_convertible.
	(is_invocable_r_v, is_nothrow_invocable_r_v): Add missing parameter.
	* testsuite/20_util/is_nothrow_convertible/value_ext.cc: New test.
	* testsuite/20_util/is_nothrow_convertible/value.cc: Check with type
	that has nothrow explicit conversion but potentially-throwing implicit
	conversion.
	* testsuite/20_util/is_nothrow_invocable/value.cc: Likewise.
	* testsuite/20_util/is_nothrow_invocable/value_ext.cc: Fix helper
	function to only consider implicit conversions.
	* testsuite/20_util/tuple/cons/noexcept_specs.cc: Add comment.

From-SVN: r271171
parent 1158c5b4
2019-05-14 Jonathan Wakely <jwakely@redhat.com> 2019-05-14 Jonathan Wakely <jwakely@redhat.com>
* include/std/type_traits (__is_nt_convertible_helper): Define it
unconditionally, not only for C++20.
(__is_nothrow_convertible): Define internal trait for use in C++11.
(__is_nt_invocable_impl: Fix by using __is_nothrow_convertible.
(is_invocable_r_v, is_nothrow_invocable_r_v): Add missing parameter.
* testsuite/20_util/is_nothrow_convertible/value_ext.cc: New test.
* testsuite/20_util/is_nothrow_convertible/value.cc: Check with type
that has nothrow explicit conversion but potentially-throwing implicit
conversion.
* testsuite/20_util/is_nothrow_invocable/value.cc: Likewise.
* testsuite/20_util/is_nothrow_invocable/value_ext.cc: Fix helper
function to only consider implicit conversions.
* testsuite/20_util/tuple/cons/noexcept_specs.cc: Add comment.
* include/std/iterator: Include <iosfwd> instead of <istream> and * include/std/iterator: Include <iosfwd> instead of <istream> and
<ostream>. <ostream>.
......
...@@ -1378,7 +1378,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1378,7 +1378,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
: public __is_convertible_helper<_From, _To>::type : public __is_convertible_helper<_From, _To>::type
{ }; { };
#if __cplusplus > 201703L
template<typename _From, typename _To, template<typename _From, typename _To,
bool = __or_<is_void<_From>, is_function<_To>, bool = __or_<is_void<_From>, is_function<_To>,
is_array<_To>>::value> is_array<_To>>::value>
...@@ -1393,7 +1392,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1393,7 +1392,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
static void __test_aux(_To1) noexcept; static void __test_aux(_To1) noexcept;
template<typename _From1, typename _To1> template<typename _From1, typename _To1>
static bool_constant<noexcept(__test_aux<_To1>(std::declval<_From1>()))> static
__bool_constant<noexcept(__test_aux<_To1>(std::declval<_From1>()))>
__test(int); __test(int);
template<typename, typename> template<typename, typename>
...@@ -1404,6 +1404,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -1404,6 +1404,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
using type = decltype(__test<_From, _To>(0)); using type = decltype(__test<_From, _To>(0));
}; };
// is_nothrow_convertible for C++11
template<typename _From, typename _To>
struct __is_nothrow_convertible
: public __is_nt_convertible_helper<_From, _To>::type
{ };
#if __cplusplus > 201703L
/// is_nothrow_convertible /// is_nothrow_convertible
template<typename _From, typename _To> template<typename _From, typename _To>
struct is_nothrow_convertible struct is_nothrow_convertible
...@@ -2831,8 +2838,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -2831,8 +2838,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct __is_nt_invocable_impl<_Result, _Ret, struct __is_nt_invocable_impl<_Result, _Ret,
__void_t<typename _Result::type>> __void_t<typename _Result::type>>
: __or_<is_void<_Ret>, : __or_<is_void<_Ret>,
__and_<is_convertible<typename _Result::type, _Ret>, __is_nothrow_convertible<typename _Result::type, _Ret>>
is_nothrow_constructible<_Ret, typename _Result::type>>>
{ }; { };
/// std::is_nothrow_invocable_r /// std::is_nothrow_invocable_r
...@@ -2852,14 +2858,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -2852,14 +2858,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
= is_nothrow_invocable<_Fn, _Args...>::value; = is_nothrow_invocable<_Fn, _Args...>::value;
/// std::is_invocable_r_v /// std::is_invocable_r_v
template<typename _Fn, typename... _Args> template<typename _Ret, typename _Fn, typename... _Args>
inline constexpr bool is_invocable_r_v inline constexpr bool is_invocable_r_v
= is_invocable_r<_Fn, _Args...>::value; = is_invocable_r<_Ret, _Fn, _Args...>::value;
/// std::is_nothrow_invocable_r_v /// std::is_nothrow_invocable_r_v
template<typename _Fn, typename... _Args> template<typename _Ret, typename _Fn, typename... _Args>
inline constexpr bool is_nothrow_invocable_r_v inline constexpr bool is_nothrow_invocable_r_v
= is_nothrow_invocable_r<_Fn, _Args...>::value; = is_nothrow_invocable_r<_Ret, _Fn, _Args...>::value;
#endif // C++17 #endif // C++17
#if __cplusplus >= 201703L #if __cplusplus >= 201703L
......
...@@ -21,9 +21,12 @@ ...@@ -21,9 +21,12 @@
#include <type_traits> #include <type_traits>
#include <testsuite_tr1.h> #include <testsuite_tr1.h>
#ifndef IS_NT_CONVERTIBLE_DEFINED
using std::is_nothrow_convertible;
#endif
void test01() void test01()
{ {
using std::is_nothrow_convertible;
using namespace __gnu_test; using namespace __gnu_test;
// Positive conversion tests. // Positive conversion tests.
...@@ -175,3 +178,16 @@ void test01() ...@@ -175,3 +178,16 @@ void test01()
NoexceptMoveConsClass&, NoexceptMoveConsClass&,
NoexceptMoveConsClass>(false)); NoexceptMoveConsClass>(false));
} }
void test02()
{
struct X { };
struct Y
{
explicit Y(X) noexcept; // not viable for implicit conversions
Y(...);
};
static_assert(!is_nothrow_convertible<X, Y>::value, "");
}
// 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-do compile { target c++11 } }
#include <type_traits>
// Test the non-standard __is_nothrow_convertible trait
template<typename From, typename To>
using is_nothrow_convertible = std::__is_nothrow_convertible<From, To>;
#define IS_NT_CONVERTIBLE_DEFINED
#include "value.cc"
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
// <http://www.gnu.org/licenses/>. // <http://www.gnu.org/licenses/>.
// { dg-options "-std=gnu++17" } // { dg-options "-std=gnu++17" }
// { dg-do compile } // { dg-do compile { target c++17 } }
#include <type_traits> #include <type_traits>
...@@ -151,4 +151,17 @@ void test01() ...@@ -151,4 +151,17 @@ void test01()
"would call private member"); "would call private member");
static_assert( ! is_nt_invocable_r<void, F, int, int >(), static_assert( ! is_nt_invocable_r<void, F, int, int >(),
"would call private member"); "would call private member");
struct FX {
X operator()() const noexcept { return {}; }
};
static_assert( is_nt_invocable< FX >(), "FX::operator() is nothrow" );
static_assert( is_nt_invocable_r<X, FX >(), "no conversion needed" );
struct Y {
explicit Y(X) noexcept; // not viable for implicit conversions
Y(...);
};
static_assert( ! is_nt_invocable_r<Y, FX >(), "conversion to Y can throw" );
} }
...@@ -23,13 +23,20 @@ template<typename... T> ...@@ -23,13 +23,20 @@ template<typename... T>
constexpr bool is_nt_invocable() constexpr bool is_nt_invocable()
{ return std::__is_nothrow_invocable<T...>::value; } { return std::__is_nothrow_invocable<T...>::value; }
template<typename R, typename... T> template<typename R, typename... T>
constexpr bool is_nt_invocable_conv(std::true_type) constexpr bool is_nt_invocable_conv(std::true_type)
{ {
using result_type = typename std::__invoke_result<T...>::type; using result_type = typename std::__invoke_result<T...>::type;
struct ConvIsNothrow
{
static void test(std::true_type, R) noexcept;
static void test(std::false_type, const result_type&);
};
return std::is_void<R>::value return std::is_void<R>::value
|| (std::is_convertible<result_type, R>::value || noexcept(ConvIsNothrow::test(std::is_convertible<result_type, R>(),
&& std::is_nothrow_constructible<R, result_type>::value); std::declval<result_type>()));
} }
template<typename R, typename... T> template<typename R, typename... T>
......
...@@ -56,7 +56,9 @@ namespace test_trait{ ...@@ -56,7 +56,9 @@ namespace test_trait{
using type = decltype(test<From, To>(0)); using type = decltype(test<From, To>(0));
}; };
/// is_nothrow_convertible // Similar to std::is_nothrow_convertible, but only considers whether the
// actual conversion can throw (and not any potential copies of From).
// This means the result is not affected by copy elision of From in C++17.
template<typename From, typename To> template<typename From, typename To>
struct is_nothrow_convertible struct is_nothrow_convertible
: public is_nt_convertible_helper<From, To>::type : public is_nt_convertible_helper<From, To>::type
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment