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. Tested powerpc64le-linux, committed to trunk.
commit e872e963111c865ac5a446703663bdc52990b938 Author: Jonathan Wakely <jwak...@redhat.com> Date: Tue May 14 14:21:53 2019 +0100 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. diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index ea733e7b7b2..f68d366269d 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -1378,7 +1378,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : public __is_convertible_helper<_From, _To>::type { }; -#if __cplusplus > 201703L template<typename _From, typename _To, bool = __or_<is_void<_From>, is_function<_To>, is_array<_To>>::value> @@ -1393,7 +1392,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION static void __test_aux(_To1) noexcept; 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); template<typename, typename> @@ -1404,6 +1404,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION 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 template<typename _From, typename _To> struct is_nothrow_convertible @@ -2831,8 +2838,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __is_nt_invocable_impl<_Result, _Ret, __void_t<typename _Result::type>> : __or_<is_void<_Ret>, - __and_<is_convertible<typename _Result::type, _Ret>, - is_nothrow_constructible<_Ret, typename _Result::type>>> + __is_nothrow_convertible<typename _Result::type, _Ret>> { }; /// std::is_nothrow_invocable_r @@ -2852,14 +2858,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION = is_nothrow_invocable<_Fn, _Args...>::value; /// std::is_invocable_r_v - template<typename _Fn, typename... _Args> + template<typename _Ret, typename _Fn, typename... _Args> 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 - template<typename _Fn, typename... _Args> + template<typename _Ret, typename _Fn, typename... _Args> 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 #if __cplusplus >= 201703L diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value.cc index b82b68c23b9..c4c2fda845c 100644 --- a/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value.cc @@ -21,9 +21,12 @@ #include <type_traits> #include <testsuite_tr1.h> +#ifndef IS_NT_CONVERTIBLE_DEFINED +using std::is_nothrow_convertible; +#endif + void test01() { - using std::is_nothrow_convertible; using namespace __gnu_test; // Positive conversion tests. @@ -175,3 +178,16 @@ void test01() NoexceptMoveConsClass&, 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, ""); +} diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value_ext.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value_ext.cc new file mode 100644 index 00000000000..50bab80596f --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/value_ext.cc @@ -0,0 +1,28 @@ +// 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" diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value.cc index 0a365a910fc..04d310fff38 100644 --- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value.cc @@ -16,7 +16,7 @@ // <http://www.gnu.org/licenses/>. // { dg-options "-std=gnu++17" } -// { dg-do compile } +// { dg-do compile { target c++17 } } #include <type_traits> @@ -151,4 +151,17 @@ void test01() "would call private member"); static_assert( ! is_nt_invocable_r<void, F, int, int >(), "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" ); } diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value_ext.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value_ext.cc index e9a4de66de7..3a133ade4de 100644 --- a/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value_ext.cc +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_invocable/value_ext.cc @@ -23,13 +23,20 @@ template<typename... T> constexpr bool is_nt_invocable() { 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) { 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 - || (std::is_convertible<result_type, R>::value - && std::is_nothrow_constructible<R, result_type>::value); + || noexcept(ConvIsNothrow::test(std::is_convertible<result_type, R>(), + std::declval<result_type>())); } template<typename R, typename... T> diff --git a/libstdc++-v3/testsuite/20_util/tuple/cons/noexcept_specs.cc b/libstdc++-v3/testsuite/20_util/tuple/cons/noexcept_specs.cc index da9ef1c26b2..d04b0aee8ed 100644 --- a/libstdc++-v3/testsuite/20_util/tuple/cons/noexcept_specs.cc +++ b/libstdc++-v3/testsuite/20_util/tuple/cons/noexcept_specs.cc @@ -56,7 +56,9 @@ namespace test_trait{ 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> struct is_nothrow_convertible : public is_nt_convertible_helper<From, To>::type