[libcxx-commits] [libcxx] r357370 - Make common_type's implementation common

Eric Fiselier via libcxx-commits libcxx-commits at lists.llvm.org
Sun Mar 31 13:49:06 PDT 2019


Author: ericwf
Date: Sun Mar 31 13:49:06 2019
New Revision: 357370

URL: http://llvm.org/viewvc/llvm-project?rev=357370&view=rev
Log:
Make common_type's implementation common 

Summary:
Currently the C++03 implementation of common_type has much different behavior than the C++11 one. This causes bugs, including inside `<chrono>`.

This patch unifies the two implementations as best it can. The more code they share, the less their behavior can diverge. 

Reviewers: mclow.lists, ldionne, sbenza

Reviewed By: mclow.lists, ldionne

Subscribers: libcxx-commits

Tags: #libc

Differential Revision: https://reviews.llvm.org/D59678

Modified:
    libcxx/trunk/include/type_traits
    libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp

Modified: libcxx/trunk/include/type_traits
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/include/type_traits?rev=357370&r1=357369&r2=357370&view=diff
==============================================================================
--- libcxx/trunk/include/type_traits (original)
+++ libcxx/trunk/include/type_traits Sun Mar 31 13:49:06 2019
@@ -2117,43 +2117,63 @@ struct _LIBCPP_TEMPLATE_VIS make_unsigne
 template <class _Tp> using make_unsigned_t = typename make_unsigned<_Tp>::type;
 #endif
 
-#ifdef _LIBCPP_HAS_NO_VARIADICS
+template <class _Tp, class _Up, class = void>
+struct __common_type2_imp {};
 
-template <class _Tp, class _Up = void, class _Vp = void>
-struct _LIBCPP_TEMPLATE_VIS common_type
-{
-public:
-    typedef typename common_type<typename common_type<_Tp, _Up>::type, _Vp>::type type;
+template <class _Tp, class _Up>
+struct __common_type2_imp<_Tp, _Up,
+                          typename __void_t<decltype(
+                                            true ? _VSTD::declval<_Tp>() : _VSTD::declval<_Up>()
+                                            )>::type>
+{
+  typedef typename decay<decltype(
+                         true ? _VSTD::declval<_Tp>() : _VSTD::declval<_Up>()
+                         )>::type type;
 };
 
-template <>
-struct _LIBCPP_TEMPLATE_VIS common_type<void, void, void>
-{
-public:
-    typedef void type;
-};
+template <class, class = void>
+struct __common_type_impl {};
 
-template <class _Tp>
-struct _LIBCPP_TEMPLATE_VIS common_type<_Tp, void, void>
-{
-public:
-    typedef typename common_type<_Tp, _Tp>::type type;
+// Clang provides variadic templates in C++03 as an extension.
+#if !defined(_LIBCPP_CXX03_LANG) || defined(__clang__)
+# define _LIBCPP_OPTIONAL_PACK(...) , __VA_ARGS__
+template <class... Tp>
+struct __common_types;
+template <class... _Tp>
+struct _LIBCPP_TEMPLATE_VIS common_type;
+#else
+# define _LIBCPP_OPTIONAL_PACK(...)
+struct __no_arg;
+template <class _Tp, class _Up, class = __no_arg>
+struct __common_types;
+template <class _Tp = __no_arg, class _Up = __no_arg, class _Vp = __no_arg,
+          class _Unused = __no_arg>
+struct common_type {
+  static_assert(sizeof(_Unused) == 0,
+                "common_type accepts at most 3 arguments in C++03");
 };
+#endif // _LIBCPP_CXX03_LANG
 
 template <class _Tp, class _Up>
-struct _LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, void>
+struct __common_type_impl<
+    __common_types<_Tp, _Up>,
+    typename __void_t<typename common_type<_Tp, _Up>::type>::type>
 {
-    typedef typename decay<decltype(
-        true ? _VSTD::declval<_Tp>() : _VSTD::declval<_Up>()
-      )>::type type;
+  typedef typename common_type<_Tp, _Up>::type type;
 };
 
-#else  // _LIBCPP_HAS_NO_VARIADICS
+template <class _Tp, class _Up, class _Vp _LIBCPP_OPTIONAL_PACK(class... _Rest)>
+struct __common_type_impl<
+    __common_types<_Tp, _Up, _Vp _LIBCPP_OPTIONAL_PACK(_Rest...)>,
+    typename __void_t<typename common_type<_Tp, _Up>::type>::type>
+    : __common_type_impl<__common_types<typename common_type<_Tp, _Up>::type,
+                                        _Vp _LIBCPP_OPTIONAL_PACK(_Rest...)> > {
+};
 
 // bullet 1 - sizeof...(Tp) == 0
 
-template <class ..._Tp>
-struct _LIBCPP_TEMPLATE_VIS common_type {};
+template <>
+struct _LIBCPP_TEMPLATE_VIS common_type<> {};
 
 // bullet 2 - sizeof...(Tp) == 1
 
@@ -2163,68 +2183,29 @@ struct _LIBCPP_TEMPLATE_VIS common_type<
 
 // bullet 3 - sizeof...(Tp) == 2
 
-template <class _Tp, class _Up, class = void>
-struct __common_type2_imp {};
-
-template <class _Tp, class _Up>
-struct __common_type2_imp<_Tp, _Up,
-    typename __void_t<decltype(
-        true ? _VSTD::declval<_Tp>() : _VSTD::declval<_Up>()
-    )>::type>
-{
-    typedef typename decay<decltype(
-        true ? _VSTD::declval<_Tp>() : _VSTD::declval<_Up>()
-    )>::type type;
-};
-
-template <class _Tp, class _Up,
-          class _DTp = typename decay<_Tp>::type,
-          class _DUp = typename decay<_Up>::type>
-using __common_type2 =
-  typename conditional<
-    is_same<_Tp, _DTp>::value && is_same<_Up, _DUp>::value,
-    __common_type2_imp<_Tp, _Up>,
-    common_type<_DTp, _DUp>
-  >::type;
-
 template <class _Tp, class _Up>
 struct _LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up>
-    : __common_type2<_Tp, _Up> {};
+    : conditional<
+        is_same<_Tp, typename decay<_Tp>::type>::value && is_same<_Up, typename decay<_Up>::type>::value,
+        __common_type2_imp<_Tp, _Up>,
+        common_type<typename decay<_Tp>::type, typename decay<_Up>::type>
+    >::type
+{};
 
 // bullet 4 - sizeof...(Tp) > 2
 
-template <class ...Tp> struct __common_types;
+template <class _Tp, class _Up, class _Vp _LIBCPP_OPTIONAL_PACK(class... _Rest)>
+struct _LIBCPP_TEMPLATE_VIS
+    common_type<_Tp, _Up, _Vp _LIBCPP_OPTIONAL_PACK(_Rest...)>
+    : __common_type_impl<
+          __common_types<_Tp, _Up, _Vp _LIBCPP_OPTIONAL_PACK(_Rest...)> > {};
 
-template <class, class = void>
-struct __common_type_impl {};
-
-template <class _Tp, class _Up>
-struct __common_type_impl<
-    __common_types<_Tp, _Up>,
-    typename __void_t<typename common_type<_Tp, _Up>::type>::type>
-{
-  typedef typename common_type<_Tp, _Up>::type type;
-};
-
-template <class _Tp, class _Up, class ..._Vp>
-struct __common_type_impl<__common_types<_Tp, _Up, _Vp...>,
-    typename __void_t<typename common_type<_Tp, _Up>::type>::type>
-  : __common_type_impl<
-      __common_types<typename common_type<_Tp, _Up>::type, _Vp...> >
-{
-
-};
-
-template <class _Tp, class _Up, class ..._Vp>
-struct _LIBCPP_TEMPLATE_VIS common_type<_Tp, _Up, _Vp...>
-    : __common_type_impl<__common_types<_Tp, _Up, _Vp...> > {};
+#undef _LIBCPP_OPTIONAL_PACK
 
 #if _LIBCPP_STD_VER > 11
 template <class ..._Tp> using common_type_t = typename common_type<_Tp...>::type;
 #endif
 
-#endif  // _LIBCPP_HAS_NO_VARIADICS
-
 // is_assignable
 
 template<typename, typename _Tp> struct __select_2nd { typedef _Tp type; };

Modified: libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp
URL: http://llvm.org/viewvc/llvm-project/libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp?rev=357370&r1=357369&r2=357370&view=diff
==============================================================================
--- libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp (original)
+++ libcxx/trunk/test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp Sun Mar 31 13:49:06 2019
@@ -48,40 +48,53 @@ namespace std
     template <> struct common_type< ::X<double>, ::X<double> > {};
 }
 
-#if TEST_STD_VER >= 11
+template <class> struct VoidT { typedef void type; };
+
+#if TEST_STD_VER < 11
+template <class Trait, class = void>
+struct no_common_type_imp : std::true_type {};
+
+template <class Trait>
+struct no_common_type_imp<Trait, typename VoidT<typename Trait::type>::type>
+    : std::false_type {};
+
+struct NoArgTag;
+
+template <class Tp = NoArgTag, class Up = NoArgTag, class Vp = NoArgTag>
+struct no_common_type : no_common_type_imp<std::common_type<Tp, Up, Vp> > {};
+template <class Tp, class Up>
+struct no_common_type<Tp, Up> : no_common_type_imp<std::common_type<Tp, Up> > {
+};
+template <class Tp>
+struct no_common_type<Tp> : no_common_type_imp<std::common_type<Tp> > {};
+template <>
+struct no_common_type<> : no_common_type_imp<std::common_type<> > {};
+#else
 template <class Tp>
 struct always_bool_imp { using type = bool; };
 template <class Tp> using always_bool = typename always_bool_imp<Tp>::type;
 
 template <class ...Args>
 constexpr auto no_common_type_imp(int)
-  -> always_bool<typename std::common_type<Args...>::type>
-  { return false; }
+-> always_bool<typename std::common_type<Args...>::type>
+{ return false; }
 
 template <class ...Args>
 constexpr bool no_common_type_imp(long) { return true; }
 
 template <class ...Args>
 using no_common_type = std::integral_constant<bool, no_common_type_imp<Args...>(0)>;
-
-template <class Tp>
-using Decay = typename std::decay<Tp>::type;
-
-template <class ...Args>
-using CommonType = typename std::common_type<Args...>::type;
+#endif
 
 template <class T1, class T2>
-struct TernaryOpImp {
-  static_assert(std::is_same<Decay<T1>, T1>::value, "must be same");
-  static_assert(std::is_same<Decay<T2>, T2>::value, "must be same");
-  using type = typename std::decay<
+struct TernaryOp {
+  static_assert((std::is_same<typename std::decay<T1>::type, T1>::value), "must be same");
+  static_assert((std::is_same<typename std::decay<T2>::type, T2>::value), "must be same");
+  typedef typename std::decay<
       decltype(false ? std::declval<T1>() : std::declval<T2>())
-    >::type;
+    >::type type;
 };
 
-template <class T1, class T2>
-using TernaryOp = typename TernaryOpImp<T1, T2>::type;
-
 // -- If sizeof...(T) is zero, there shall be no member type.
 void test_bullet_one() {
   static_assert(no_common_type<>::value, "");
@@ -90,23 +103,23 @@ void test_bullet_one() {
 // If sizeof...(T) is one, let T0 denote the sole type constituting the pack T.
 // The member typedef-name type shall denote the same type as decay_t<T0>.
 void test_bullet_two() {
-  static_assert(std::is_same<CommonType<void>, void>::value, "");
-  static_assert(std::is_same<CommonType<int>, int>::value, "");
-  static_assert(std::is_same<CommonType<int const>, int>::value, "");
-  static_assert(std::is_same<CommonType<int volatile[]>, int volatile*>::value, "");
-  static_assert(std::is_same<CommonType<void(&)()>, void(*)()>::value, "");
+  static_assert((std::is_same<std::common_type<void>::type, void>::value), "");
+  static_assert((std::is_same<std::common_type<int>::type, int>::value), "");
+  static_assert((std::is_same<std::common_type<int const>::type, int>::value), "");
+  static_assert((std::is_same<std::common_type<int volatile[]>::type, int volatile*>::value), "");
+  static_assert((std::is_same<std::common_type<void(&)()>::type, void(*)()>::value), "");
 
-  static_assert(no_common_type<X<double> >::value, "");
+  static_assert((no_common_type<X<double> >::value), "");
 }
 
 template <class T, class U, class Expect>
 void test_bullet_three_one_imp() {
-  using DT = Decay<T>;
-  using DU = Decay<U>;
-  static_assert(!std::is_same<T, DT>::value || !std::is_same<U, DU>::value, "");
-  static_assert(std::is_same<CommonType<T, U>, Expect>::value, "");
-  static_assert(std::is_same<CommonType<U, T>, Expect>::value, "");
-  static_assert(std::is_same<CommonType<T, U>, CommonType<DT, DU>>::value, "");
+  typedef typename std::decay<T>::type DT;
+  typedef typename std::decay<U>::type DU;
+  static_assert((!std::is_same<T, DT>::value || !std::is_same<U, DU>::value), "");
+  static_assert((std::is_same<typename std::common_type<T, U>::type, Expect>::value), "");
+  static_assert((std::is_same<typename std::common_type<U, T>::type, Expect>::value), "");
+  static_assert((std::is_same<typename std::common_type<T, U>::type, typename std::common_type<DT, DU>::type>::value), "");
 }
 
 // (3.3)
@@ -120,30 +133,30 @@ void test_bullet_three_one() {
   // Test that the user provided specialization of common_type is used after
   // decaying T1.
   {
-    using T1 = S<int> const;
-    using T2 = int;
+    typedef const S<int> T1;
+    typedef int T2;
     test_bullet_three_one_imp<T1, T2, S<int> >();
   }
   // Test a user provided specialization that does not provide a typedef.
   {
-    using T1 = ::S<long> const;
-    using T2 = long;
-    static_assert(no_common_type<T1, T2>::value, "");
-    static_assert(no_common_type<T2, T1>::value, "");
+    typedef const ::S<long> T1;
+    typedef long T2;
+    static_assert((no_common_type<T1, T2>::value), "");
+    static_assert((no_common_type<T2, T1>::value), "");
   }
   // Test that the ternary operator is not applied when the types are the
   // same.
   {
-    using T1 = const void;
-    using Expect = void;
-    static_assert(std::is_same<CommonType<T1, T1>, Expect>::value, "");
-    static_assert(std::is_same<CommonType<T1, T1>, CommonType<T1>>::value, "");
+    typedef const void T1;
+    typedef void Expect;
+    static_assert((std::is_same<std::common_type<T1, T1>::type, Expect>::value), "");
+    static_assert((std::is_same<std::common_type<T1, T1>::type, std::common_type<T1>::type>::value), "");
   }
   {
-    using T1 = int const[];
-    using Expect = int const*;
-    static_assert(std::is_same<CommonType<T1, T1>, Expect>::value, "");
-    static_assert(std::is_same<CommonType<T1, T1>, CommonType<T1>>::value, "");
+    typedef int const T1[];
+    typedef int const* Expect;
+    static_assert((std::is_same<std::common_type<T1, T1>::type, Expect>::value), "");
+    static_assert((std::is_same<std::common_type<T1, T1>::type, std::common_type<T1>::type>::value), "");
   }
 }
 
@@ -158,32 +171,32 @@ void test_bullet_three_one() {
 //       decay_t<decltype(false ? declval<D1>() : declval<D2>())>
 void test_bullet_three_two() {
   {
-    using T1 = int const*;
-    using T2 = int*;
-    using Expect = TernaryOp<T1, T2>;
-    static_assert(std::is_same<CommonType<T1, T2>, Expect>::value, "");
-    static_assert(std::is_same<CommonType<T2, T1>, Expect>::value, "");
+    typedef int const* T1;
+    typedef int* T2;
+    typedef TernaryOp<T1, T2>::type Expect;
+    static_assert((std::is_same<std::common_type<T1, T2>::type, Expect>::value), "");
+    static_assert((std::is_same<std::common_type<T2, T1>::type, Expect>::value), "");
   }
   // Test that there is no ::type member when the ternary op is ill-formed
   {
-    using T1 = int;
-    using T2 = void;
-    static_assert(no_common_type<T1, T2>::value, "");
-    static_assert(no_common_type<T2, T1>::value, "");
+    typedef int T1;
+    typedef void T2;
+    static_assert((no_common_type<T1, T2>::value), "");
+    static_assert((no_common_type<T2, T1>::value), "");
   }
   {
-    using T1 = int;
-    using T2 = X<int>;
-    static_assert(no_common_type<T1, T2>::value, "");
-    static_assert(no_common_type<T2, T1>::value, "");
+    typedef int T1;
+    typedef X<int> T2;
+    static_assert((no_common_type<T1, T2>::value), "");
+    static_assert((no_common_type<T2, T1>::value), "");
   }
   // Test that the ternary operator is not applied when the types are the
   // same.
   {
-    using T1 = void;
-    using Expect = void;
-    static_assert(std::is_same<CommonType<T1, T1>, Expect>::value, "");
-    static_assert(std::is_same<CommonType<T1, T1>, CommonType<T1>>::value, "");
+    typedef void T1;
+    typedef void Expect;
+    static_assert((std::is_same<std::common_type<T1, T1>::type, Expect>::value), "");
+    static_assert((std::is_same<std::common_type<T1, T1>::type, std::common_type<T1>::type>::value), "");
   }
 }
 
@@ -196,10 +209,12 @@ void test_bullet_three_two() {
 // no member type.
 void test_bullet_four() {
   { // test that there is no ::type member
-    static_assert(no_common_type<int, E>::value, "");
-    static_assert(no_common_type<int, int, E>::value, "");
-    static_assert(no_common_type<int, int, E, int>::value, "");
-    static_assert(no_common_type<int, int, int, E>::value, "");
+    static_assert((no_common_type<int, E>::value), "");
+    static_assert((no_common_type<int, int, E>::value), "");
+#if TEST_STD_VER >= 11
+    static_assert((no_common_type<int, int, E, int>::value), "");
+    static_assert((no_common_type<int, int, int, E>::value), "");
+#endif
   }
 }
 
@@ -207,8 +222,8 @@ void test_bullet_four() {
 // The example code specified in Note B for common_type
 namespace note_b_example {
 
-using PF1 = bool (&)();
-using PF2 = short (*)(long);
+typedef bool (&PF1)();
+typedef short (*PF2)(long);
 
 struct S {
   operator PF2() const;
@@ -217,22 +232,24 @@ struct S {
   char data;
 };
 
-using PMF = void (S::*)(long) const;
-using PMD = char S::*;
+typedef void (S::*PMF)(long) const;
+typedef char S::*PMD;
 
 using std::is_same;
 using std::result_of;
 using std::unique_ptr;
 
-static_assert(is_same<typename result_of<S(int)>::type, short>::value, "Error!");
-static_assert(is_same<typename result_of<S&(unsigned char, int&)>::type, double>::value, "Error!");
-static_assert(is_same<typename result_of<PF1()>::type, bool>::value, "Error!");
-static_assert(is_same<typename result_of<PMF(unique_ptr<S>, int)>::type, void>::value, "Error!");
-static_assert(is_same<typename result_of<PMD(S)>::type, char&&>::value, "Error!");
-static_assert(is_same<typename result_of<PMD(const S*)>::type, const char&>::value, "Error!");
+static_assert((is_same<result_of<S(int)>::type, short>::value), "Error!");
+static_assert((is_same<result_of<S&(unsigned char, int&)>::type, double>::value), "Error!");
+static_assert((is_same<result_of<PF1()>::type, bool>::value), "Error!");
+static_assert((is_same<result_of<PMF(unique_ptr<S>, int)>::type, void>::value), "Error!");
+#if TEST_STD_VER >= 11
+static_assert((is_same<result_of<PMD(S)>::type, char&&>::value), "Error!");
+#endif
+static_assert((is_same<result_of<PMD(const S*)>::type, const char&>::value), "Error!");
 
 } // namespace note_b_example
-#endif // TEST_STD_VER >= 11
+
 
 int main(int, char**)
 {
@@ -286,13 +303,12 @@ int main(int, char**)
     static_assert((std::is_same<std::common_type<int, S<int>, S<int> >::type, S<int> >::value), "");
     static_assert((std::is_same<std::common_type<int, int, S<int> >::type, S<int> >::value), "");
 
-#if TEST_STD_VER >= 11
+
   test_bullet_one();
   test_bullet_two();
   test_bullet_three_one();
   test_bullet_three_two();
   test_bullet_four();
-#endif
 
 //  P0548
     static_assert((std::is_same<std::common_type<S<int> >::type,         S<int> >::value), "");
@@ -308,5 +324,10 @@ int main(int, char**)
     static_assert((std::is_same<std::common_type<int, const int>::type,       int>::value), "");
     static_assert((std::is_same<std::common_type<const int, const int>::type, int>::value), "");
 
+#if TEST_STD_VER >= 11
+    // Test that we're really variadic in C++11
+    static_assert(std::is_same<std::common_type<int, int, int, int, int, int, int, int>::type, int>::value, "");
+#endif
+
   return 0;
 }




More information about the libcxx-commits mailing list