[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