Message ID | 20190206172527.GA18972@redhat.com |
---|---|
State | New |
Headers | show |
Series | PR libstdc++/89102 fix common_type<> and common_type<T> specializations | expand |
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 }
diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits index f05a583cb04..bc2250d9dce 100644 --- a/libstdc++-v3/include/std/type_traits +++ b/libstdc++-v3/include/std/type_traits @@ -2132,9 +2132,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION struct __expanded_common_type_wrapper<__failure_type, _Args...> { typedef __failure_type type; }; + template<> + struct common_type<> + { }; + template<typename _Tp> struct common_type<_Tp> - { typedef typename decay<_Tp>::type type; }; + : common_type<_Tp, _Tp> + { }; template<typename _Tp, typename _Up> struct common_type<_Tp, _Up> diff --git a/libstdc++-v3/testsuite/20_util/common_type/requirements/explicit_instantiation.cc b/libstdc++-v3/testsuite/20_util/common_type/requirements/explicit_instantiation.cc index 28535a80d64..dc57ab5ad8e 100644 --- a/libstdc++-v3/testsuite/20_util/common_type/requirements/explicit_instantiation.cc +++ b/libstdc++-v3/testsuite/20_util/common_type/requirements/explicit_instantiation.cc @@ -30,6 +30,7 @@ namespace std typedef void test_type5; typedef const void test_type6; + template struct common_type<>; template struct common_type<test_type1>; template struct common_type<test_type1, test_type2>; template struct common_type<test_type1, test_type2, test_type3>; 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 372c1a58378..9b94eb13434 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 @@ -159,7 +159,10 @@ namespace std { }; } +static_assert(is_type<std::common_type<int>, int>(), ""); +static_assert(is_type<std::common_type<const int>, int>(), ""); static_assert(is_type<std::common_type<int, int>, int>(), ""); +static_assert(is_type<std::common_type<const int, int>, int>(), ""); static_assert(is_type<std::common_type<ScEn, ScEn>, ScEn>(), ""); static_assert(is_type<std::common_type<UnscEn, UnscEn>, UnscEn>(), ""); static_assert(is_type<std::common_type<UnscEn, int>, int>(), ""); @@ -180,6 +183,8 @@ static_assert(is_type<std::common_type<int*, const volatile int*>, const volatile int*>(), ""); static_assert(is_type<std::common_type<void*, const volatile int*>, const volatile void*>(), ""); +static_assert(is_type<std::common_type<void>, void>(), ""); +static_assert(is_type<std::common_type<const void>, void>(), ""); static_assert(is_type<std::common_type<void, void>, void>(), ""); static_assert(is_type<std::common_type<const void, const void>, void>(), ""); static_assert(is_type<std::common_type<int&, int&&>, int>(), ""); @@ -316,6 +321,14 @@ static_assert(!has_type<std::common_type<UConv1, Abstract&&>>(), ""); static_assert(!has_type<std::common_type<std::initializer_list<int>, std::initializer_list<long>>>(), ""); +// PR libstdc++/89102 +static_assert(!has_type<std::common_type<int() &>>(), ""); +static_assert(!has_type<std::common_type<int() & noexcept>>(), ""); +static_assert(!has_type<std::common_type<int() const>>(), ""); +static_assert(!has_type<std::common_type<int(...) &>>(), ""); +static_assert(!has_type<std::common_type<int(...) & noexcept>>(), ""); +static_assert(!has_type<std::common_type<int(...) const>>(), ""); + void test(int i) { auto local_lmd1 = [=](int, double) { return i + i; }; diff --git a/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_2.cc b/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_2.cc index 64c8ea9339e..8f6f4ecd4d8 100644 --- a/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_2.cc +++ b/libstdc++-v3/testsuite/20_util/common_type/requirements/sfinae_friendly_2.cc @@ -25,7 +25,7 @@ template<typename... Args> constexpr std::array<typename std::common_type<Args...>::type, sizeof...(Args)> -make_array(Args&&... args) // { dg-error "invalid use" } +make_array(Args&&... args) // { dg-error "no type.*common_type<>" } { typedef typename std::common_type<Args...>::type CT; return std::array<CT, sizeof...(Args)>{static_cast<CT>