The exception specifications for _Not_fn::operator() should consider whether operator! on the result can throw.
* include/std/functional (_Not_fn): Make exception specifications depend on whether negating the result can throw. * testsuite/20_util/not_fn/1.cc: Move to ... * testsuite/20_util/function_objects/not_fn/1.cc: ... here. Add tests for types that can throw when negated and that cannot be negated. Tested powerpc64le-linux, committed to trunk.
commit 333bf1f483cb3ba215fe959867310c7d2dd99e58 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu Oct 13 10:34:43 2016 +0100 Fix exception-specifications for std::_Not_fn * include/std/functional (_Not_fn): Make exception specifications depend on whether negating the result can throw. * testsuite/20_util/not_fn/1.cc: Move to ... * testsuite/20_util/function_objects/not_fn/1.cc: ... here. Add tests for types that can throw when negated and that cannot be negated. diff --git a/libstdc++-v3/include/std/functional b/libstdc++-v3/include/std/functional index 6a45314..58134b7 100644 --- a/libstdc++-v3/include/std/functional +++ b/libstdc++-v3/include/std/functional @@ -2140,6 +2140,16 @@ _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type) template<typename _Fn> class _Not_fn { + template<typename _Tp> + using __is_nothrow_negatable + = __bool_constant<noexcept(!std::declval<_Tp>())>; + + template<typename _Fn2, typename... _Args> + using __noexcept_cond = __and_< + __is_nothrow_callable<_Fn2(_Args&&...)>, + __is_nothrow_negatable<result_of_t<_Fn2(_Args&&...)>> + >; + public: template<typename _Fn2> _Not_fn(_Fn2&& __fn, int) @@ -2152,21 +2162,21 @@ _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type) template<typename... _Args> auto operator()(_Args&&... __args) & - noexcept(__is_nothrow_callable<_Fn&(_Args&&...)>::value) + noexcept(__noexcept_cond<_Fn&, _Args&&...>::value) -> decltype(!std::declval<result_of_t<_Fn&(_Args&&...)>>()) { return !std::__invoke(_M_fn, std::forward<_Args>(__args)...); } template<typename... _Args> auto operator()(_Args&&... __args) const & - noexcept(__is_nothrow_callable<const _Fn&(_Args&&...)>::value) + noexcept(__noexcept_cond<const _Fn&, _Args&&...>::value) -> decltype(!std::declval<result_of_t<const _Fn&(_Args&&...)>>()) { return !std::__invoke(_M_fn, std::forward<_Args>(__args)...); } template<typename... _Args> auto operator()(_Args&&... __args) && - noexcept(__is_nothrow_callable<_Fn&&(_Args&&...)>::value) + noexcept(__noexcept_cond<_Fn&&, _Args&&...>::value) -> decltype(!std::declval<result_of_t<_Fn&&(_Args&&...)>>()) { return !std::__invoke(std::move(_M_fn), @@ -2176,7 +2186,7 @@ _GLIBCXX_MEM_FN_TRAITS(&&, false_type, true_type) template<typename... _Args> auto operator()(_Args&&... __args) const && - noexcept(__is_nothrow_callable<const _Fn&&(_Args&&...)>::value) + noexcept(__noexcept_cond<const _Fn&&, _Args&&...>::value) -> decltype(!std::declval<result_of_t<const _Fn&&(_Args&&...)>>()) { return !std::__invoke(std::move(_M_fn), diff --git a/libstdc++-v3/testsuite/20_util/function_objects/not_fn/1.cc b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/1.cc new file mode 100644 index 0000000..246b962 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/function_objects/not_fn/1.cc @@ -0,0 +1,133 @@ +// Copyright (C) 2014-2016 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" } + +#include <functional> +#include <testsuite_hooks.h> + +using std::not_fn; + +int func(int, char) { return 0; } + +struct F +{ + bool operator()() { return false; } + bool operator()() const { return true; } + bool operator()(int) { return false; } +}; + +void +test01() +{ + auto f1 = not_fn(func); + VERIFY( f1(1, '2') == true ); + + auto f2 = not_fn( [] { return true; } ); + VERIFY( f2() == false ); + + auto f3 = not_fn( F{} ); + VERIFY( f3() == true ); + VERIFY( f3(1) == true ); + const auto f4 = f3; + VERIFY( f4() == false ); +} + +template<typename F, typename Arg> +auto foo(F f, Arg arg) -> decltype(not_fn(f)(arg)) { return not_fn(f)(arg); } + +template<typename F, typename Arg> +auto foo(F f, Arg arg) -> decltype(not_fn(f)()) { return not_fn(f)(); } + +struct negator +{ + bool operator()(int) const { return false; } + void operator()() const {} +}; + +void +test02() +{ + foo(negator{}, 1); // PR libstdc++/66998 +} + +void +test03() +{ + struct X { bool b; }; + X x{ false }; + VERIFY( not_fn(&X::b)(x) ); +} + +void +test04() +{ + struct abstract { virtual void f() = 0; }; + struct derived : abstract { void f() { } }; + struct F { bool operator()(abstract&) { return false; } }; + F f; + derived d; + VERIFY( not_fn(f)(d) ); +} + +void +test05() +{ + auto nf = std::not_fn([] { return false; }); + auto copy(nf); // PR libstdc++/70564 +} + +void +test06() +{ + struct Boolean { + Boolean operator!() noexcept(false) { return *this; } + }; + struct F { + Boolean operator()() { return {}; } + }; + F f; + auto notf = std::not_fn(f); + using NotF = decltype(notf); + static_assert( std::is_callable<NotF()>::value, "cannot negate" ); + static_assert( !noexcept(notf()), "conversion to bool affects noexcept" ); +} + +void +test07() +{ + struct NonNegatable { }; + struct F { + NonNegatable operator()() { return {}; } + }; + F f; + auto notf = std::not_fn(f); + using NotF = decltype(notf); + static_assert( !std::is_callable<NotF()>::value, "cannot negate" ); +} + +int +main() +{ + test01(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); +} diff --git a/libstdc++-v3/testsuite/20_util/not_fn/1.cc b/libstdc++-v3/testsuite/20_util/not_fn/1.cc deleted file mode 100644 index 233a6d3..0000000 --- a/libstdc++-v3/testsuite/20_util/not_fn/1.cc +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (C) 2014-2016 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" } - -#include <functional> -#include <testsuite_hooks.h> - -using std::not_fn; - -int func(int, char) { return 0; } - -struct F -{ - bool operator()() { return false; } - bool operator()() const { return true; } - bool operator()(int) { return false; } -}; - -void -test01() -{ - auto f1 = not_fn(func); - VERIFY( f1(1, '2') == true ); - - auto f2 = not_fn( [] { return true; } ); - VERIFY( f2() == false ); - - auto f3 = not_fn( F{} ); - VERIFY( f3() == true ); - VERIFY( f3(1) == true ); - const auto f4 = f3; - VERIFY( f4() == false ); -} - -template<typename F, typename Arg> -auto foo(F f, Arg arg) -> decltype(not_fn(f)(arg)) { return not_fn(f)(arg); } - -template<typename F, typename Arg> -auto foo(F f, Arg arg) -> decltype(not_fn(f)()) { return not_fn(f)(); } - -struct negator -{ - bool operator()(int) const { return false; } - void operator()() const {} -}; - -void -test02() -{ - foo(negator{}, 1); // PR libstdc++/66998 -} - -void -test03() -{ - struct X { bool b; }; - X x{ false }; - VERIFY( not_fn(&X::b)(x) ); -} - -void -test04() -{ - struct abstract { virtual void f() = 0; }; - struct derived : abstract { void f() { } }; - struct F { bool operator()(abstract&) { return false; } }; - F f; - derived d; - VERIFY( not_fn(f)(d) ); -} - -void -test05() -{ - auto nf = std::not_fn([] { return false; }); - auto copy(nf); // PR libstdc++/70564 -} - -int -main() -{ - test01(); - test02(); - test03(); - test04(); - test05(); -}