This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [PATCH] PR libstdc++/89102 fix common_type<> and common_type<T> specializations


On 06/02/19 17:25 +0000, Jonathan Wakely wrote:
This is a partial implementation of the revised std::common_type rules
from P0435R1.

	PR libstdc++/89102 (partial)
	* include/std/type_traits (common_type<>): Define.
	(common_type<T>): Derive from common_type<T, T>.
	* testsuite/20_util/common_type/requirements/explicit_instantiation.cc:
	Test zero-length template argument list.
	* testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc:
	Test additional single argument cases.
	* testsuite/20_util/common_type/requirements/sfinae_friendly_2.cc:
	Adjust expected error.

Tested powerpc64le-linux, committed to trunk.

I think this could be backported to gcc-8-branch too.

I have a complete patch for stage 1 which I'll post in a minute.

This is the complee patch, but it changes the semantics in a few cases
involving user specializations, so I'll wait for stage 1 (and then
maybe backport it to gcc-9-branch later).


commit 6d5355fc251047d71c5262a38e6c4fe590a20905
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Feb 6 11:51:37 2019 +0000

    PR libstdc++/89102 implement new common_type rules (P0435R1, P0548R1)
    
    This change ensures that std::common_type<> is a complete type (LWG
    2408), and that std::common_type<T>, std::common_type<cv T1, cv T2>, and
    std::common_type<T1, T2, R...> will use program-defined specializations
    for std::common_type<T1, T2> (LWG 2465).
    
    The implementation of common_type<T1, T2, R...> is changed to use
    void_t, and the specializations for duration and time_point are modified
    to also use void_t instead of depending on implementation details of
    common_type.
    
            PR libstdc++/89102
            * include/std/chrono (__duration_common_type_wrapper): Replace with ...
            (__duration_common_type): New helper.
            (common_type<chrono::duration<R1, P2>, chrono::duration<R2, P2>>): Use
            __duration_common_type.
            (__timepoint_common_type_wrapper): Replace with ...
            (__timepoint_common_type): New helper.
            (common_type<chrono::time_point<C, D2>, chrono::time_point<C, D2>>):
            Use __time_point_common_type.
            * include/std/type_traits (common_type<>): Define, as per LWG 2408.
            (__common_type_impl): If either argument is transformed by decay,
            use the common_type of the decayed types.
            (__common_type_impl<_Tp, _Up, _Tp, _Up>): If the types are already
            decayed, use __do_common_type_impl to get the common_type.
            (common_type<_Tp>): Use common_type<_Tp, _Tp>.
            (__do_member_type_wrapper, __member_type_wrapper)
            (__expanded_common_type_wrapper): Remove.
            (__common_type_pack, __common_type_fold): New helpers.
            (common_type<_Tp, _Up, _Vp...>): Use new helpers instead of
            __member_type_wrapper and __expanded_common_type_wrapper.
            * testsuite/20_util/common_type/requirements/explicit_instantiation.cc:
            Test zero-length template argument list.
            * testsuite/20_util/common_type/requirements/sfinae_friendly_1.cc:
            Test single argument cases and argument types that should decay.
            * testsuite/20_util/common_type/requirements/sfinae_friendly_2.cc:
            Adjust expected error.
            * testsuite/20_util/duration/literals/range_neg.cc: Use zero for
            dg-error lineno.
            * testsuite/20_util/duration/requirements/typedefs_neg1.cc: Likewise.
            * testsuite/20_util/duration/requirements/typedefs_neg2.cc: Likewise.
            * testsuite/20_util/duration/requirements/typedefs_neg3.cc: Likewise.

diff --git a/libstdc++-v3/include/std/chrono b/libstdc++-v3/include/std/chrono
index 9e63fa9c698..8cef79ea3e6 100644
--- a/libstdc++-v3/include/std/chrono
+++ b/libstdc++-v3/include/std/chrono
@@ -67,48 +67,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   // 20.11.4.3 specialization of common_type (for duration, sfinae-friendly)
 
+  template<typename _CT, typename _Period1, typename _Period2, typename = void>
+    struct __duration_common_type
+    { };
+
   template<typename _CT, typename _Period1, typename _Period2>
-    struct __duration_common_type_wrapper
+    struct __duration_common_type<_CT, _Period1, _Period2,
+				  __void_t<typename _CT::type>>
     {
     private:
-      typedef __static_gcd<_Period1::num, _Period2::num> __gcd_num;
-      typedef __static_gcd<_Period1::den, _Period2::den> __gcd_den;
-      typedef typename _CT::type __cr;
-      typedef ratio<__gcd_num::value,
-        (_Period1::den / __gcd_den::value) * _Period2::den> __r;
+      using __gcd_num = __static_gcd<_Period1::num, _Period2::num>;
+      using __gcd_den = __static_gcd<_Period1::den, _Period2::den>;
+      using __cr = typename _CT::type;
+      using __r = ratio<__gcd_num::value,
+			(_Period1::den / __gcd_den::value) * _Period2::den>;
+
     public:
-      typedef __success_type<chrono::duration<__cr, __r>> type;
+      using type = chrono::duration<__cr, __r>;
     };
 
   template<typename _Period1, typename _Period2>
-    struct __duration_common_type_wrapper<__failure_type, _Period1, _Period2>
+    struct __duration_common_type<__failure_type, _Period1, _Period2>
     { typedef __failure_type type; };
 
   template<typename _Rep1, typename _Period1, typename _Rep2, typename _Period2>
     struct common_type<chrono::duration<_Rep1, _Period1>,
-             chrono::duration<_Rep2, _Period2>>
-    : public __duration_common_type_wrapper<typename __member_type_wrapper<
-             common_type<_Rep1, _Rep2>>::type, _Period1, _Period2>::type
+		       chrono::duration<_Rep2, _Period2>>
+    : __duration_common_type<common_type<_Rep1, _Rep2>, _Period1, _Period2>
     { };
 
   // 20.11.4.3 specialization of common_type (for time_point, sfinae-friendly)
 
-  template<typename _CT, typename _Clock>
-    struct __timepoint_common_type_wrapper
-    {
-      typedef __success_type<chrono::time_point<_Clock, typename _CT::type>>
-        type;
-    };
+  template<typename _CT, typename _Clock, typename = void>
+    struct __timepoint_common_type
+    { };
 
-  template<typename _Clock>
-    struct __timepoint_common_type_wrapper<__failure_type, _Clock>
-    { typedef __failure_type type; };
+  template<typename _CT, typename _Clock>
+    struct __timepoint_common_type<_CT, _Clock, __void_t<typename _CT::type>>
+    {
+      using type = chrono::time_point<_Clock, typename _CT::type>;
+    };
 
   template<typename _Clock, typename _Duration1, typename _Duration2>
     struct common_type<chrono::time_point<_Clock, _Duration1>,
-             chrono::time_point<_Clock, _Duration2>>
-    : public __timepoint_common_type_wrapper<typename __member_type_wrapper<
-             common_type<_Duration1, _Duration2>>::type, _Clock>::type
+		       chrono::time_point<_Clock, _Duration2>>
+    : __timepoint_common_type<common_type<_Duration1, _Duration2>, _Clock>
     { };
 
   namespace chrono
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index bc2250d9dce..6ab3b0008ac 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2082,6 +2082,9 @@ _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;
@@ -2091,65 +2094,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   struct __do_common_type_impl
   {
     template<typename _Tp, typename _Up>
-      static __success_type<typename decay<decltype
-			    (true ? std::declval<_Tp>()
-			     : std::declval<_Up>())>::type> _S_test(int);
+      using __cond_t
+	= decltype(true ? std::declval<_Tp>() : std::declval<_Up>());
+
+    template<typename _Tp, typename _Up>
+      static __success_type<typename decay<__cond_t<_Tp, _Up>>::type>
+      _S_test(int);
 
     template<typename, typename>
-      static __failure_type _S_test(...);
+      static __failure_type
+      _S_test(...);
   };
 
-  template<typename _Tp, typename _Up>
-    struct __common_type_impl
-    : private __do_common_type_impl
-    {
-      typedef decltype(_S_test<_Tp, _Up>(0)) type;
-    };
-
-  struct __do_member_type_wrapper
-  {
-    template<typename _Tp>
-      static __success_type<typename _Tp::type> _S_test(int);
-
-    template<typename>
-      static __failure_type _S_test(...);
-  };
-
-  template<typename _Tp>
-    struct __member_type_wrapper
-    : private __do_member_type_wrapper
-    {
-      typedef decltype(_S_test<_Tp>(0)) type;
-    };
-
-  template<typename _CTp, typename... _Args>
-    struct __expanded_common_type_wrapper
-    {
-      typedef common_type<typename _CTp::type, _Args...> type;
-    };
-
-  template<typename... _Args>
-    struct __expanded_common_type_wrapper<__failure_type, _Args...>
-    { typedef __failure_type type; };
-
+  // If sizeof...(T) is zero, there shall be no member type.
   template<>
     struct common_type<>
     { };
 
-  template<typename _Tp>
-    struct common_type<_Tp>
-    : common_type<_Tp, _Tp>
+  // If sizeof...(T) is one, the same type, if any, as common_type_t<T0, T0>.
+  template<typename _Tp0>
+    struct common_type<_Tp0>
+    : public common_type<_Tp0, _Tp0>
     { };
 
-  template<typename _Tp, typename _Up>
-    struct common_type<_Tp, _Up>
-    : public __common_type_impl<_Tp, _Up>::type
+  // If sizeof...(T) is two, ...
+  template<typename _Tp1, typename _Tp2,
+	   typename _Dp1 = typename decay<_Tp1>::type,
+	   typename _Dp2 = typename decay<_Tp2>::type>
+    struct __common_type_impl
+    {
+      // If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false,
+      // let C denote the same type, if any, as common_type_t<D1, D2>.
+      using type = common_type<_Dp1, _Dp2>;
+    };
+
+  template<typename _Tp1, typename _Tp2>
+    struct __common_type_impl<_Tp1, _Tp2, _Tp1, _Tp2>
+    : private __do_common_type_impl
+    {
+      // Otherwise, if decay_t<decltype(false ? declval<D1>() : declval<D2>())>
+      // denotes a valid type, let C denote that type.
+      using type = decltype(_S_test<_Tp1, _Tp2>(0));
+    };
+
+  // If sizeof...(T) is two, ...
+  template<typename _Tp1, typename _Tp2>
+    struct common_type<_Tp1, _Tp2>
+    : public __common_type_impl<_Tp1, _Tp2>::type
     { };
 
-  template<typename _Tp, typename _Up, typename... _Vp>
-    struct common_type<_Tp, _Up, _Vp...>
-    : public __expanded_common_type_wrapper<typename __member_type_wrapper<
-               common_type<_Tp, _Up>>::type, _Vp...>::type
+  template<typename...>
+    struct __common_type_pack
+    { };
+
+  template<typename, typename, typename = void>
+    struct __common_type_fold;
+
+  // If sizeof...(T) is greater than two, ...
+  template<typename _Tp1, typename _Tp2, typename... _Rp>
+    struct common_type<_Tp1, _Tp2, _Rp...>
+    : public __common_type_fold<common_type<_Tp1, _Tp2>,
+				__common_type_pack<_Rp...>>
+    { };
+
+  // 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,
+  // as common_type_t<C, R...>.
+  template<typename _CTp, typename... _Rp>
+    struct __common_type_fold<_CTp, __common_type_pack<_Rp...>,
+			      __void_t<typename _CTp::type>>
+    : public common_type<typename _CTp::type, _Rp...>
+    { };
+
+  // Otherwise, there shall be no member type.
+  template<typename _CTp, typename _Rp>
+    struct __common_type_fold<_CTp, _Rp, void>
     { };
 
   /// The underlying type of an enum.
@@ -2414,9 +2433,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<bool _Cond, typename _Tp = void>
     using __enable_if_t = typename enable_if<_Cond, _Tp>::type;
 
-  // __void_t (std::void_t for C++11)
-  template<typename...> using __void_t = void;
-
 #if __cplusplus >= 201703L || !defined(__STRICT_ANSI__) // c++17 or gnu++11
 #define __cpp_lib_void_t 201411
   /// A metafunction that always yields void, used for detecting valid types.
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 9b94eb13434..a54da7c24b2 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
@@ -214,6 +214,7 @@ static_assert(is_type<std::common_type<void(&&)(), void(&)()>,
 static_assert(is_type<std::common_type<void(&&)(), void(&&)()>,
 	      void(*)()>(), "");
 static_assert(is_type<std::common_type<ImplicitTo<int>, int>, int>(), "");
+static_assert(is_type<std::common_type<const ImplicitTo<int>, int>, int>(), "");
 static_assert(is_type<std::common_type<ImplicitTo<int>, ImplicitTo<int>>,
 	      ImplicitTo<int>>(), "");
 static_assert(is_type<std::common_type<ImplicitTo<int>, int,
@@ -260,19 +261,22 @@ 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>(), "");
+static_assert(is_type<std::common_type<X1&, const X2>, RX12>(), "");
+static_assert(is_type<std::common_type<const X1&, const X2&>, RX12>(), "");
 static_assert(is_type<std::common_type<X2, X1>, RX21>(), "");
 
 static_assert(is_type<std::common_type<X1, X2, X1>, Y1>(), "");
 static_assert(is_type<std::common_type<X2, X1, X1>, Y3>(), "");
 
 static_assert(is_type<std::common_type<X1, X1, X2>, RX12>(), "");
+static_assert(is_type<std::common_type<X1&, const X1, const X2&&>, RX12>(), "");
 static_assert(is_type<std::common_type<X1, X1, X2, X1>, Y1>(), "");
 
 static_assert(!has_type<std::common_type<>>(), "");
 static_assert(!has_type<std::common_type<int, S>>(), "");
 static_assert(!has_type<std::common_type<U, S>>(), "");
 static_assert(!has_type<std::common_type<U, U2>>(), "");
-static_assert(!has_type<std::common_type<const ImplicitTo<int>, int>>(), "");
 static_assert(!has_type<std::common_type<PrivateImplicitTo<int>, int>>(), "");
 static_assert(!has_type<std::common_type<const PrivateImplicitTo<int>,
 	      int>>(), "");
diff --git a/libstdc++-v3/testsuite/20_util/duration/literals/range_neg.cc b/libstdc++-v3/testsuite/20_util/duration/literals/range_neg.cc
index 1c3ee90c4a6..8be1be5423f 100644
--- a/libstdc++-v3/testsuite/20_util/duration/literals/range_neg.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/literals/range_neg.cc
@@ -26,6 +26,6 @@ test01()
 
   // std::numeric_limits<int64_t>::max() == 9223372036854775807;
   auto h = 9223372036854775808h;
-  // { dg-error "cannot be represented" "" { target *-*-* } 908 }
+  // { dg-error "cannot be represented by duration" "" { target *-*-* } 0 }
 }
 // { dg-prune-output "in .constexpr. expansion" } // needed for -O0
diff --git a/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg1.cc b/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg1.cc
index 9181488a612..9c25552892d 100644
--- a/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg1.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg1.cc
@@ -29,4 +29,4 @@ void test01()
   test_type d; // { dg-error "required from here" }
 }
 
-// { dg-error "rep cannot be a duration" "" { target *-*-* } 316 }
+// { dg-error "rep cannot be a duration" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg2.cc b/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg2.cc
index e3e450ebf6b..e6029fc782a 100644
--- a/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg2.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg2.cc
@@ -30,5 +30,5 @@ void test01()
   test_type d;			// { dg-error "required from here" }
 }
 
-// { dg-error "must be a specialization of ratio" "" { target *-*-* } 317 }
+// { dg-error "must be a specialization of ratio" "" { target *-*-* } 0 }
 // { dg-prune-output "not a member" }
diff --git a/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg3.cc b/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg3.cc
index de0c6659b24..ed8baa195c7 100644
--- a/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg3.cc
+++ b/libstdc++-v3/testsuite/20_util/duration/requirements/typedefs_neg3.cc
@@ -31,4 +31,4 @@ void test01()
   test_type d;  // { dg-error "required from here" }
 }
 
-// { dg-error "period must be positive" "" { target *-*-* } 319 }
+// { dg-error "period must be positive" "" { target *-*-* } 0 }

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]