The x86 attributes such as ms_abi, stdcall, fastcall etc. alter the function type, which means that functions with one of those attributes do not match any of the partial specializations of std::is_function.
Rather than duplicating the list for every calling convention, this adds a fallback to the std::is_function primary template which identifies other function types. The fallback works by assuming that all function types fall into one of two categories: referenceable and abominable. The former can be detected by testing for function-to-pointer decay, and the latter are non-referenceable types that are not cv void. In order to detect referenceable types it's necessary to replace the current definition of __is_referenceable with one that doesn't depend on std::is_function, to avoid a cycle. The definition of std::decay can also be modified to only act on referenceable function types, because abominable function types do not decay. PR libstdc++/91371 * include/std/type_traits (__declval, declval, __void_t): Declare earlier in the file. (__is_referenceable): Rewrite to not depend on is_function. (__is_referenceable_function): New trait to identify non-abominable function types. (__is_qualified_function): New alias to identify abominable function types. (is_function): Make primary template use __is_referenceable_function and __is_qualified_function to detect function types not covered by the partial specializations. (__decay_selector): Use __is_referenceable_function instead of is_function. (__decay_selector<_Up, false, true>): Do not use add_pointer. * testsuite/20_util/bind/91371.cc: New test. * testsuite/20_util/is_function/91371.cc: New test. * testsuite/20_util/is_function/value.cc: Check more pointer types. * testsuite/20_util/is_member_function_pointer/91371.cc: New test. Tested x86_64-linux. Not committed yet. I'd like to hear Daniel's thoughts on this approach, as he wrote the original __is_referenceable trait, and much of <type_traits>. This new __is_referenceable simply uses void_t<T&> to detect whether forming T& is valid. The detection for function-to-pointer decay works by checking whether static_cast<T*>(declval<T&>()) is well-formed. If T is not a class type (which could have a conversion operator) and is not nullptr or cv void*cv (which can convert to nullptr* and cv void*cv* repectively) then it must be a function. The detection for abominable function types assumes that all types are referenceable except functions with cv- or ref-qualifiers and cv void types. So if it's not referenceable and not void, it's an abominable function type.
commit 2c8c1d0ef4b9ed6e80abf3694618c0e63859de78 Author: Jonathan Wakely <jwak...@redhat.com> Date: Fri Aug 16 15:03:54 2019 +0100 PR libstdc++/91371 make std::is_function handle other calling conventions The x86 attributes such as ms_abi, stdcall, fastcall etc. alter the function type, which means that functions with one of those attributes do not match any of the partial specializations of std::is_function. Rather than duplicating the list for every calling convention, add a fallback to the std::is_function primary template which identifies other function types. The fallback works by assuming that all function types fall into one of two categories: referenceable and abominable. The former can be detected by testing for function-to-pointer decay, and the latter are non-referenceable types that are not cv void. In order to detect referenceable types it's necessary to replace the current definition of __is_referenceable with one that doesn't depend on std::is_function, to avoid a cycle. The definition of std::decay can also be modified to only act on referenceable function types, because abominable function types do not decay. PR libstdc++/91371 * include/std/type_traits (__declval, declval, __void_t): Declare earlier in the file. (__is_referenceable): Rewrite to not depend on is_function. (__is_referenceable_function): New trait to identify non-abominable function types. (__is_qualified_function): New alias to identify abominable function types. (is_function): Make primary template use __is_referenceable_function and __is_qualified_function to detect function types not covered by the partial specializations. (__decay_selector): Use __is_referenceable_function instead of is_function. (__decay_selector<_Up, false, true>): Do not use add_pointer. * testsuite/20_util/bind/91371.cc: New test. * testsuite/20_util/is_function/91371.cc: New test. * testsuite/20_util/is_function/value.cc: Check more pointer types. * testsuite/20_util/is_member_function_pointer/91371.cc: New test. diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 44db2cade5d..ca16cb7ccc1 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -480,10 +480,64 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION : public integral_constant<bool, __is_class(_Tp)> { }; + template<typename _Tp, typename _Up = _Tp&&> + _Up + __declval(int); + + template<typename _Tp> + _Tp + __declval(long); + + /** + * @brief Utility to simplify expressions used in unevaluated operands + * @ingroup utilities + */ + template<typename _Tp> + auto declval() noexcept -> decltype(__declval<_Tp>(0)); + + // __void_t (std::void_t for C++11) + template<typename...> using __void_t = void; + + template<typename _Tp> struct is_null_pointer; + + // Utility to detect referenceable types ([defns.referenceable]). + template<typename _Tp, typename = void> + struct __is_referenceable + : false_type { }; + + template<typename _Tp> + struct __is_referenceable<_Tp, __void_t<_Tp&>> + : true_type { }; + + // A function type that does not have cv-qualifiers or a ref-qualifier. + template<typename _Tp, + bool = __is_class(_Tp) || __is_union(_Tp), + typename = void> + struct __is_referenceable_function + : false_type { }; + + template<typename _Tp> + struct __is_referenceable_function<_Tp, false, + __void_t<decltype(static_cast<_Tp*>(std::declval<_Tp&>()))>> + : __not_<__or_<is_pointer<_Tp>, is_null_pointer<_Tp>>>::type { }; + + // A function type with cv-qualifiers and/or a ref-qualifier. + // This assumes that only types that are not referenceable are cv void types + // and qualified function types. + template<typename _Tp> + using __is_qualified_function + = __not_<__or_<__is_referenceable<_Tp>, is_void<_Tp>>>; + /// is_function - template<typename> + template<typename _Tp> struct is_function - : public false_type { }; + : __or_<__is_referenceable_function<_Tp>, + __is_qualified_function<_Tp>>::type + { }; + + // Define partial specializations for most function types. + // The primary template is used for function types with alternative + // calling conventions (such as x86 stdcall) and for non-function types. template<typename _Res, typename... _ArgTypes _GLIBCXX_NOEXCEPT_PARM> struct is_function<_Res(_ArgTypes...) _GLIBCXX_NOEXCEPT_QUAL> @@ -705,24 +759,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #endif >; - - // Utility to detect referenceable types ([defns.referenceable]). - - template<typename _Tp> - struct __is_referenceable - : public __or_<is_object<_Tp>, is_reference<_Tp>>::type - { }; - - template<typename _Res, typename... _Args _GLIBCXX_NOEXCEPT_PARM> - struct __is_referenceable<_Res(_Args...) _GLIBCXX_NOEXCEPT_QUAL> - : public true_type - { }; - - template<typename _Res, typename... _Args _GLIBCXX_NOEXCEPT_PARM> - struct __is_referenceable<_Res(_Args......) _GLIBCXX_NOEXCEPT_QUAL> - : public true_type - { }; - // Type properties. /// is_const @@ -841,22 +877,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // Destructible and constructible type properties. - /** - * @brief Utility to simplify expressions used in unevaluated operands - * @ingroup utilities - */ - - template<typename _Tp, typename _Up = _Tp&&> - _Up - __declval(int); - - template<typename _Tp> - _Tp - __declval(long); - - template<typename _Tp> - auto declval() noexcept -> decltype(__declval<_Tp>(0)); - template<typename, unsigned = 0> struct extent; @@ -2186,7 +2206,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION // in make_pair, make_tuple, etc. template<typename _Up, bool _IsArray = is_array<_Up>::value, - bool _IsFunction = is_function<_Up>::value> + bool _IsFunction = __is_referenceable_function<_Up>::value> struct __decay_selector; // NB: DR 705. @@ -2200,7 +2220,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Up> struct __decay_selector<_Up, false, true> - { typedef typename add_pointer<_Up>::type __type; }; + { typedef _Up* __type; }; /// decay template<typename _Tp> @@ -2261,9 +2281,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct conditional<false, _Iftrue, _Iffalse> { typedef _Iffalse type; }; - // __void_t (std::void_t for C++11) - template<typename...> using __void_t = void; - /// common_type template<typename... _Tp> struct common_type; diff --git a/libstdc++-v3/testsuite/20_util/bind/91371.cc b/libstdc++-v3/testsuite/20_util/bind/91371.cc new file mode 100644 index 00000000000..1c6f55e9ece --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/bind/91371.cc @@ -0,0 +1,37 @@ +// 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 i?86-*-* x86_64-*-* } } +// { dg-require-effective-target c++11 } + +#include <functional> + +int bar(int) __attribute__((ms_abi)); +int baz(int) __attribute__((sysv_abi)); + +void +test01() +{ + // PR libstdc++/91371 + std::bind(bar, 5)(); + std::bind(baz, 5)(); + + static_assert(std::is_function<decltype(bar)>::value, ""); + static_assert(std::is_function<decltype(baz)>::value, ""); + static_assert(std::is_pointer<std::decay_t<decltype(bar)>>::value, ""); + static_assert(std::is_pointer<std::decay_t<decltype(baz)>>::value, ""); +} diff --git a/libstdc++-v3/testsuite/20_util/is_function/91371.cc b/libstdc++-v3/testsuite/20_util/is_function/91371.cc new file mode 100644 index 00000000000..c51d373027b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_function/91371.cc @@ -0,0 +1,47 @@ +// 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 i?86-*-* x86_64-*-* } } +// { dg-require-effective-target c++11 } + +#include <type_traits> + +using std::is_function; + +#ifdef __i386__ +static_assert(is_function<void __attribute__((thiscall)) ()>::value, ""); +static_assert(is_function<void __attribute__((thiscall)) () const>::value, ""); +static_assert(is_function<void __attribute__((fastcall)) ()>::value, ""); +static_assert(is_function<void __attribute__((fastcall)) () const>::value, ""); +static_assert(is_function<void __attribute__((stdcall)) ()>::value, ""); +static_assert(is_function<void __attribute__((stdcall)) () const>::value, ""); +#endif +static_assert(is_function<void __attribute__((ms_abi)) ()>::value, ""); +static_assert(is_function<void __attribute__((ms_abi)) () const>::value, ""); +static_assert(is_function<void __attribute__((ms_abi)) () const &>::value, ""); +static_assert(is_function<void __attribute__((ms_abi)) () &&>::value, ""); +static_assert(is_function<void __attribute__((sysv_abi)) ()>::value, ""); +static_assert(is_function<void __attribute__((sysv_abi)) () const>::value, ""); + +struct X { operator X*(); }; +static_assert(!is_function<X>::value, ""); +static_assert(!is_function<X&>::value, ""); +static_assert(!is_function<X*>::value, ""); +union Y { operator Y*(); int i; long l;}; +static_assert(!is_function<Y>::value, ""); +static_assert(!is_function<Y&>::value, ""); +static_assert(!is_function<Y*>::value, ""); diff --git a/libstdc++-v3/testsuite/20_util/is_function/value.cc b/libstdc++-v3/testsuite/20_util/is_function/value.cc index 7b94b58b6cb..4a3bb7c5740 100644 --- a/libstdc++-v3/testsuite/20_util/is_function/value.cc +++ b/libstdc++-v3/testsuite/20_util/is_function/value.cc @@ -37,12 +37,18 @@ void test01() char (int, ClassType) const volatile &&>(true), ""); // Negative tests. + static_assert(test_category<is_function, int*>(false), ""); static_assert(test_category<is_function, int&>(false), ""); static_assert(test_category<is_function, void>(false), ""); static_assert(test_category<is_function, const void>(false), ""); + static_assert(test_category<is_function, void*>(false), ""); + static_assert(test_category<is_function, const void*>(false), ""); + static_assert(test_category<is_function, void**>(false), ""); + static_assert(test_category<is_function, std::nullptr_t>(false), ""); static_assert(test_category<is_function, AbstractClass>(false), ""); static_assert(test_category<is_function, int(&)(int)>(false), ""); + static_assert(test_category<is_function, int(*)(int)>(false), ""); // Sanity check. static_assert(test_category<is_function, ClassType>(false), ""); diff --git a/libstdc++-v3/testsuite/20_util/is_member_function_pointer/91371.cc b/libstdc++-v3/testsuite/20_util/is_member_function_pointer/91371.cc new file mode 100644 index 00000000000..25fecc11cd8 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_member_function_pointer/91371.cc @@ -0,0 +1,35 @@ +// 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 i?86-*-* x86_64-*-* } } +// { dg-require-effective-target c++11 } + +#include <type_traits> + +struct Z +{ + void __attribute__((ms_abi)) f() const { } + void __attribute__((sysv_abi)) g() const { } +#ifdef __i386__ + void __attribute__((thiscall)) h() const { } +#endif +}; +static_assert( std::is_member_function_pointer<decltype(&Z::f)>::value, "" ); +static_assert( std::is_member_function_pointer<decltype(&Z::g)>::value, "" ); +#ifdef __i386__ +static_assert( std::is_member_function_pointer<decltype(&Z::h)>::value, "" ); +#endif