Final bits for libstdc/71579 std::common_type assertions attempt to give a proper 'required from here' hint for user code, do not bring many changes to the implementation and check all the template parameters for completeness. In some cases the type could be checked for completeness more than once. This seems to be unsolvable due to the fact that std::common_type could be specialized by the user, so we have to call std::common_type recursively, potentially repeating the check for the first type.
std::common_reference assertions make sure that we detect incomplete types even if the user specialized the std::basic_common_reference. Changelog: 2020-11-12 Antony Polukhin <antosh...@gmail.com> PR libstdc/71579 * include/std/type_traits (is_convertible, is_nothrow_convertible) (common_type, common_reference): Add static_asserts to make sure that the arguments of the type traits are not misused with incomplete types. * testsuite/20_util/common_reference/incomplete_basic_common_neg.cc: New test. * testsuite/20_util/common_reference/incomplete_neg.cc: New test. * testsuite/20_util/common_type/incomplete_neg.cc: New test. * testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc: Remove SFINAE tests on incomplete types. * testsuite/20_util/is_convertible/incomplete_neg.cc: New test. * testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc: New test. -- Best regards, Antony Polukhin
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index 34e068b..00fa7f5 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -1406,12 +1406,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _From, typename _To> struct is_convertible : public __is_convertible_helper<_From, _To>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_From>{}), + "first template argument must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_To>{}), + "second template argument must be a complete class or an unbounded array"); + }; // helper trait for unique_ptr<T[]>, shared_ptr<T[]>, and span<T, N> template<typename _ToElementType, typename _FromElementType> using __is_array_convertible - = is_convertible<_FromElementType(*)[], _ToElementType(*)[]>; + = typename __is_convertible_helper< + _FromElementType(*)[], _ToElementType(*)[]>::type; template<typename _From, typename _To, bool = __or_<is_void<_From>, is_function<_To>, @@ -1454,7 +1460,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _From, typename _To> struct is_nothrow_convertible : public __is_nt_convertible_helper<_From, _To>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_From>{}), + "first template argument must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_To>{}), + "second template argument must be a complete class or an unbounded array"); + }; /// is_nothrow_convertible_v template<typename _From, typename _To> @@ -2239,7 +2250,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<typename _Tp1, typename _Tp2> struct common_type<_Tp1, _Tp2> : public __common_type_impl<_Tp1, _Tp2>::type - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp1>{}), + "each argument type must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp2>{}), + "each argument type must be a complete class or an unbounded array"); + }; template<typename...> struct __common_type_pack @@ -2253,7 +2269,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct common_type<_Tp1, _Tp2, _Rp...> : public __common_type_fold<common_type<_Tp1, _Tp2>, __common_type_pack<_Rp...>> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp1>{}), + "first argument type must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp2>{}), + "second argument type must be a complete class or an unbounded array"); +#ifdef __cpp_fold_expressions + static_assert((std::__is_complete_or_unbounded( + __type_identity<_Rp>{}) && ...), + "each argument type must be a complete class or an unbounded array"); +#endif + }; // Let C denote the same type, if any, as common_type_t<T1, T2>. // If there is such a type C, type shall denote the same type, if any, @@ -3315,9 +3341,10 @@ template <typename _From, typename _To> // If A and B are both rvalue reference types, ... template<typename _Xp, typename _Yp> - struct __common_ref_impl<_Xp&&, _Yp&&, - _Require<is_convertible<_Xp&&, __common_ref_C<_Xp, _Yp>>, - is_convertible<_Yp&&, __common_ref_C<_Xp, _Yp>>>> + struct __common_ref_impl<_Xp&&, _Yp&&, _Require< + typename __is_convertible_helper<_Xp&&, __common_ref_C<_Xp, _Yp>>::type, + typename __is_convertible_helper<_Yp&&, __common_ref_C<_Xp, _Yp>>::type + >> { using type = __common_ref_C<_Xp, _Yp>; }; // let D be COMMON-REF(const X&, Y&) @@ -3326,8 +3353,9 @@ template <typename _From, typename _To> // If A is an rvalue reference and B is an lvalue reference, ... template<typename _Xp, typename _Yp> - struct __common_ref_impl<_Xp&&, _Yp&, - _Require<is_convertible<_Xp&&, __common_ref_D<_Xp, _Yp>>>> + struct __common_ref_impl<_Xp&&, _Yp&, _Require< + typename __is_convertible_helper<_Xp&&, __common_ref_D<_Xp, _Yp>>::type + >> { using type = __common_ref_D<_Xp, _Yp>; }; // If A is an lvalue reference and B is an rvalue reference, ... @@ -3374,7 +3402,12 @@ template <typename _From, typename _To> // If sizeof...(T) is one ... template<typename _Tp0> struct common_reference<_Tp0> - { using type = _Tp0; }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp0>{}), + "each argument type must be a complete class or an unbounded array"); + + using type = _Tp0; + }; template<typename _Tp1, typename _Tp2, int _Bullet = 1, typename = void> struct __common_reference_impl @@ -3385,7 +3418,12 @@ template <typename _From, typename _To> template<typename _Tp1, typename _Tp2> struct common_reference<_Tp1, _Tp2> : __common_reference_impl<_Tp1, _Tp2> - { }; + { + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp1>{}), + "each argument type must be a complete class or an unbounded array"); + static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp2>{}), + "each argument type must be a complete class or an unbounded array"); + }; // If T1 and T2 are reference types and COMMON-REF(T1, T2) is well-formed, ... template<typename _Tp1, typename _Tp2> diff --git a/libstdc++-v3/testsuite/20_util/common_reference/incomplete_basic_common_neg.cc b/libstdc++-v3/testsuite/20_util/common_reference/incomplete_basic_common_neg.cc new file mode 100644 index 0000000..9724ad0 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/common_reference/incomplete_basic_common_neg.cc @@ -0,0 +1,38 @@ +// { dg-do compile { target c++20 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +template <> +struct std::basic_common_reference<X, X> { + using type = int; +}; + +void test01() +{ + std::common_reference<X>(); // { dg-error "required from here" } + std::common_reference<X, X>(); // { dg-error "required from here" } + std::common_reference<X, X, X>(); // { dg-error "required from here" } + std::common_reference<X, X, X, X>(); // { dg-error "required from here" } + std::common_reference<X, X, X, X, X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/common_reference/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/common_reference/incomplete_neg.cc new file mode 100644 index 0000000..c151d4a --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/common_reference/incomplete_neg.cc @@ -0,0 +1,58 @@ +// { dg-do compile { target c++20 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; +class Y {}; + +void test01() +{ + std::common_reference<X>(); // { dg-error "required from here" } + std::common_reference<X, int>(); // { dg-error "required from here" } + std::common_reference<X, int, int>(); // { dg-error "required from here" } + std::common_reference<X, int, int, int>(); // { dg-error "required from here" } + std::common_reference<X, int, int, int, int>(); // { dg-error "required from here" } + + std::common_reference<int, X>(); // { dg-error "required from here" } + std::common_reference<int, X, int>(); // { dg-error "required from here" } + std::common_reference<int, X, int, int>(); // { dg-error "required from here" } + std::common_reference<int, X, int, int, int>(); // { dg-error "required from here" } + + std::common_reference<int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, X, int>(); // { dg-error "required from here" } + std::common_reference<int, int, X, int, int>(); // { dg-error "required from here" } + + std::common_reference<int, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, int, X, int>(); // { dg-error "required from here" } + + std::common_reference<int, int, int, int, X>(); // { dg-error "required from here" } + + std::common_reference<X, int, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, X, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, X, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, int, X, X>(); // { dg-error "required from here" } + + std::common_reference<Y, int, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, Y, int, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, Y, int, X>(); // { dg-error "required from here" } + std::common_reference<int, int, int, Y, X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/common_type/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/common_type/incomplete_neg.cc new file mode 100644 index 0000000..6c2641d --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/common_type/incomplete_neg.cc @@ -0,0 +1,59 @@ +// { dg-do compile { target c++17 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; +class Y {}; + +// Some of the following asserts require __cpp_fold_expressions to trigger +void test01() +{ + std::common_type<X>(); // { dg-error "required from here" } + std::common_type<X, int>(); // { dg-error "required from here" } + std::common_type<X, int, int>(); // { dg-error "required from here" } + std::common_type<X, int, int, int>(); // { dg-error "required from here" } + std::common_type<X, int, int, int, int>(); // { dg-error "required from here" } + + std::common_type<int, X>(); // { dg-error "required from here" } + std::common_type<int, X, int>(); // { dg-error "required from here" } + std::common_type<int, X, int, int>(); // { dg-error "required from here" } + std::common_type<int, X, int, int, int>(); // { dg-error "required from here" } + + std::common_type<int, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, X, int>(); // { dg-error "required from here" } + std::common_type<int, int, X, int, int>(); // { dg-error "required from here" } + + std::common_type<int, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, int, X, int>(); // { dg-error "required from here" } + + std::common_type<int, int, int, int, X>(); // { dg-error "required from here" } + + std::common_type<X, int, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, X, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, X, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, int, X, X>(); // { dg-error "required from here" } + + std::common_type<Y, int, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, Y, int, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, Y, int, X>(); // { dg-error "required from here" } + std::common_type<int, int, int, Y, X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc b/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc index 04eef50..a52f186 100644 --- a/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc +++ b/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc @@ -89,7 +89,7 @@ enum class ScEn; enum UnscEn : int; -struct Ukn; + union U { @@ -240,7 +240,7 @@ static_assert(is_type<std::common_type<decltype(nullptr), int (B::*)() const>, static_assert(is_type<std::common_type<decltype(nullptr), const int B::*>, const int B::*>(), ""); static_assert(is_type<std::common_type<Abstract&, Abstract&>, Abstract>(), ""); -static_assert(is_type<std::common_type<Ukn&, Ukn&>, Ukn>(), ""); + static_assert(is_type<std::common_type<ImplicitTo<B&>, B&>, B>(), ""); static_assert(is_type<std::common_type<ImplicitTo<B&>&, B&&>, B>(), ""); static_assert(is_type<std::common_type<UConv1, const Abstract*&>, @@ -254,11 +254,6 @@ static_assert(is_type<std::common_type<const Abstract&&, const Abstract&&>, Abstract>(), ""); static_assert(is_type<std::common_type<volatile Abstract&&, volatile Abstract&&>, Abstract>(), ""); -static_assert(is_type<std::common_type<Ukn&&, Ukn&&>, Ukn>(), ""); -static_assert(is_type<std::common_type<const Ukn&&, const Ukn&&>, - Ukn>(), ""); -static_assert(is_type<std::common_type<volatile Ukn&&, volatile Ukn&&>, - Ukn>(), ""); static_assert(is_type<std::common_type<X1, X2>, RX12>(), ""); static_assert(is_type<std::common_type<const X1, X2>, RX12>(), ""); @@ -280,13 +275,13 @@ static_assert(!has_type<std::common_type<U, U2>>(), ""); static_assert(!has_type<std::common_type<PrivateImplicitTo<int>, int>>(), ""); static_assert(!has_type<std::common_type<const PrivateImplicitTo<int>, int>>(), ""); -static_assert(!has_type<std::common_type<int, Ukn>>(), ""); + static_assert(!has_type<std::common_type<int, Abstract>>(), ""); -static_assert(!has_type<std::common_type<Ukn, Abstract>>(), ""); + static_assert(!has_type<std::common_type<int, void>>(), ""); static_assert(!has_type<std::common_type<int, const volatile void>>(), ""); static_assert(!has_type<std::common_type<Abstract, void>>(), ""); -static_assert(!has_type<std::common_type<Ukn, void>>(), ""); + static_assert(!has_type<std::common_type<int[4], void>>(), ""); static_assert(!has_type<std::common_type<ScEn, void>>(), ""); static_assert(!has_type<std::common_type<UnscEn, void>>(), ""); diff --git a/libstdc++-v3/testsuite/20_util/is_convertible/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_convertible/incomplete_neg.cc new file mode 100644 index 0000000..7fe7f8b --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_convertible/incomplete_neg.cc @@ -0,0 +1,31 @@ +// { dg-do compile { target c++11 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +void test01() +{ + std::is_convertible<X, int>(); // { dg-error "required from here" } + std::is_convertible<int, X>(); // { dg-error "required from here" } + std::is_convertible<X, X>(); // { dg-error "required from here" } +} diff --git a/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc new file mode 100644 index 0000000..9c1d2f4 --- /dev/null +++ b/libstdc++-v3/testsuite/20_util/is_nothrow_convertible/incomplete_neg.cc @@ -0,0 +1,31 @@ +// { dg-do compile { target c++20 } } + +// Copyright (C) 2020 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-error "must be a complete class" "" { target *-*-* } 0 } + +#include <type_traits> + +class X; + +void test01() +{ + std::is_nothrow_convertible<X, int>(); // { dg-error "required from here" } + std::is_nothrow_convertible<int, X>(); // { dg-error "required from here" } + std::is_nothrow_convertible<X, X>(); // { dg-error "required from here" } +}