[PATCH] PR libstdc++/71579 assert that type traits are not misused with an incomplete type

Antony Polukhin antoshkka@gmail.com
Fri May 7 18:00:00 GMT 2021


Rebased the patch on current master. Please review.

Changelog stays the same:

New 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.

2020-11-12  Antony Polukhin  <antoshkka@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.

пт, 8 янв. 2021 г. в 20:28, Antony Polukhin <antoshkka@gmail.com>:
>
>
> On Thu, Nov 12, 2020, 21:55 Antony Polukhin <antoshkka@gmail.com> wrote:
>>
>> Final bits for libstdc/71579
>
>
> Gentle reminder on last patch



-- 
Best regards,
Antony Polukhin
-------------- next part --------------
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index eaf06fc..a95d327 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,
@@ -3352,9 +3378,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&)
@@ -3363,8 +3390,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, ...
@@ -3411,7 +3439,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
@@ -3422,7 +3455,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 037a92f..b3b9671 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" }
+}


More information about the Gcc-patches mailing list