Commit f61a12b3 by Jonathan Wakely Committed by Jonathan Wakely

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
	* doc/xml/manual/intro.xml: Document DR 2408 and 2465 changes.
	* 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.

From-SVN: r270987
parent f7f36e3d
2019-05-07 Jonathan Wakely <jwakely@redhat.com> 2019-05-07 Jonathan Wakely <jwakely@redhat.com>
PR libstdc++/89102
* doc/xml/manual/intro.xml: Document DR 2408 and 2465 changes.
* 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.
* doc/xml/manual/intro.xml: Fix DR 2537 and DR 2566 confusion. * doc/xml/manual/intro.xml: Fix DR 2537 and DR 2566 confusion.
2019-05-01 Nina Dinka Ranns <dinka.ranns@gmail.com> 2019-05-01 Nina Dinka Ranns <dinka.ranns@gmail.com>
......
...@@ -1045,6 +1045,17 @@ requirements of the license of GCC. ...@@ -1045,6 +1045,17 @@ requirements of the license of GCC.
<listitem><para>Remove <code>explicit</code> from the constructor. <listitem><para>Remove <code>explicit</code> from the constructor.
</para></listitem></varlistentry> </para></listitem></varlistentry>
<varlistentry xml:id="manual.bugs.dr2408"><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2408">2408</link>:
<emphasis>SFINAE-friendly
<code>common_type</code>/<code>iterator_traits</code>
is missing in C++14
</emphasis>
</term>
<listitem><para>Make <code>iterator_traits</code> empty if any of the
types is not present in the iterator.
Make <code>common_type&lt;&gt;</code> empty.
</para></listitem></varlistentry>
<varlistentry xml:id="manual.bugs.dr2415"><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2415">2415</link>: <varlistentry xml:id="manual.bugs.dr2415"><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2415">2415</link>:
<emphasis>Inconsistency between <code>unique_ptr</code> and <code>shared_ptr</code></emphasis> <emphasis>Inconsistency between <code>unique_ptr</code> and <code>shared_ptr</code></emphasis>
</term> </term>
...@@ -1105,6 +1116,15 @@ requirements of the license of GCC. ...@@ -1105,6 +1116,15 @@ requirements of the license of GCC.
<listitem><para>Add debug mode assertion. <listitem><para>Add debug mode assertion.
</para></listitem></varlistentry> </para></listitem></varlistentry>
<varlistentry xml:id="manual.bugs.dr2465"><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2465">2465</link>:
<emphasis>SFINAE-friendly <code>common_type</code> is nearly impossible
to specialize correctly and regresses key functionality
</emphasis>
</term>
<listitem><para>Detect whether <code>decay_t</code> changes either type
and use the decayed types if so.
</para></listitem></varlistentry>
<varlistentry xml:id="manual.bugs.dr2466"><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2466">2466</link>: <varlistentry xml:id="manual.bugs.dr2466"><term><link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="&DR;#2466">2466</link>:
<emphasis><code>allocator_traits::max_size()</code> default behavior is incorrect <emphasis><code>allocator_traits::max_size()</code> default behavior is incorrect
</emphasis> </emphasis>
......
...@@ -67,48 +67,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -67,48 +67,51 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// 20.11.4.3 specialization of common_type (for duration, sfinae-friendly) // 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> 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: private:
typedef __static_gcd<_Period1::num, _Period2::num> __gcd_num; using __gcd_num = __static_gcd<_Period1::num, _Period2::num>;
typedef __static_gcd<_Period1::den, _Period2::den> __gcd_den; using __gcd_den = __static_gcd<_Period1::den, _Period2::den>;
typedef typename _CT::type __cr; using __cr = typename _CT::type;
typedef ratio<__gcd_num::value, using __r = ratio<__gcd_num::value,
(_Period1::den / __gcd_den::value) * _Period2::den> __r; (_Period1::den / __gcd_den::value) * _Period2::den>;
public: public:
typedef __success_type<chrono::duration<__cr, __r>> type; using type = chrono::duration<__cr, __r>;
}; };
template<typename _Period1, typename _Period2> 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; }; { typedef __failure_type type; };
template<typename _Rep1, typename _Period1, typename _Rep2, typename _Period2> template<typename _Rep1, typename _Period1, typename _Rep2, typename _Period2>
struct common_type<chrono::duration<_Rep1, _Period1>, struct common_type<chrono::duration<_Rep1, _Period1>,
chrono::duration<_Rep2, _Period2>> chrono::duration<_Rep2, _Period2>>
: public __duration_common_type_wrapper<typename __member_type_wrapper< : __duration_common_type<common_type<_Rep1, _Rep2>, _Period1, _Period2>
common_type<_Rep1, _Rep2>>::type, _Period1, _Period2>::type
{ }; { };
// 20.11.4.3 specialization of common_type (for time_point, sfinae-friendly) // 20.11.4.3 specialization of common_type (for time_point, sfinae-friendly)
template<typename _CT, typename _Clock, typename = void>
struct __timepoint_common_type
{ };
template<typename _CT, typename _Clock> template<typename _CT, typename _Clock>
struct __timepoint_common_type_wrapper struct __timepoint_common_type<_CT, _Clock, __void_t<typename _CT::type>>
{ {
typedef __success_type<chrono::time_point<_Clock, typename _CT::type>> using type = chrono::time_point<_Clock, typename _CT::type>;
type;
}; };
template<typename _Clock>
struct __timepoint_common_type_wrapper<__failure_type, _Clock>
{ typedef __failure_type type; };
template<typename _Clock, typename _Duration1, typename _Duration2> template<typename _Clock, typename _Duration1, typename _Duration2>
struct common_type<chrono::time_point<_Clock, _Duration1>, struct common_type<chrono::time_point<_Clock, _Duration1>,
chrono::time_point<_Clock, _Duration2>> chrono::time_point<_Clock, _Duration2>>
: public __timepoint_common_type_wrapper<typename __member_type_wrapper< : __timepoint_common_type<common_type<_Duration1, _Duration2>, _Clock>
common_type<_Duration1, _Duration2>>::type, _Clock>::type
{ }; { };
namespace chrono namespace chrono
......
...@@ -2106,6 +2106,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -2106,6 +2106,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct conditional<false, _Iftrue, _Iffalse> struct conditional<false, _Iftrue, _Iffalse>
{ typedef _Iffalse type; }; { typedef _Iffalse type; };
// __void_t (std::void_t for C++11)
template<typename...> using __void_t = void;
/// common_type /// common_type
template<typename... _Tp> template<typename... _Tp>
struct common_type; struct common_type;
...@@ -2115,65 +2118,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -2115,65 +2118,81 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
struct __do_common_type_impl struct __do_common_type_impl
{ {
template<typename _Tp, typename _Up> template<typename _Tp, typename _Up>
static __success_type<typename decay<decltype using __cond_t
(true ? std::declval<_Tp>() = decltype(true ? std::declval<_Tp>() : std::declval<_Up>());
: std::declval<_Up>())>::type> _S_test(int);
template<typename _Tp, typename _Up>
static __success_type<typename decay<__cond_t<_Tp, _Up>>::type>
_S_test(int);
template<typename, typename> template<typename, typename>
static __failure_type _S_test(...); static __failure_type
_S_test(...);
}; };
template<typename _Tp, typename _Up> // If sizeof...(T) is zero, there shall be no member type.
struct __common_type_impl template<>
: private __do_common_type_impl struct common_type<>
{ { };
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> // If sizeof...(T) is one, the same type, if any, as common_type_t<T0, T0>.
static __failure_type _S_test(...); template<typename _Tp0>
}; struct common_type<_Tp0>
: public common_type<_Tp0, _Tp0>
{ };
template<typename _Tp> // If sizeof...(T) is two, ...
struct __member_type_wrapper template<typename _Tp1, typename _Tp2,
: private __do_member_type_wrapper typename _Dp1 = typename decay<_Tp1>::type,
typename _Dp2 = typename decay<_Tp2>::type>
struct __common_type_impl
{ {
typedef decltype(_S_test<_Tp>(0)) type; // 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 _CTp, typename... _Args> template<typename _Tp1, typename _Tp2>
struct __expanded_common_type_wrapper struct __common_type_impl<_Tp1, _Tp2, _Tp1, _Tp2>
: private __do_common_type_impl
{ {
typedef common_type<typename _CTp::type, _Args...> type; // 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));
}; };
template<typename... _Args> // If sizeof...(T) is two, ...
struct __expanded_common_type_wrapper<__failure_type, _Args...> template<typename _Tp1, typename _Tp2>
{ typedef __failure_type type; }; struct common_type<_Tp1, _Tp2>
: public __common_type_impl<_Tp1, _Tp2>::type
{ };
template<> template<typename...>
struct common_type<> struct __common_type_pack
{ }; { };
template<typename _Tp> template<typename, typename, typename = void>
struct common_type<_Tp> struct __common_type_fold;
: common_type<_Tp, _Tp>
// 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...>>
{ }; { };
template<typename _Tp, typename _Up> // Let C denote the same type, if any, as common_type_t<T1, T2>.
struct common_type<_Tp, _Up> // If there is such a type C, type shall denote the same type, if any,
: public __common_type_impl<_Tp, _Up>::type // 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...>
{ }; { };
template<typename _Tp, typename _Up, typename... _Vp> // Otherwise, there shall be no member type.
struct common_type<_Tp, _Up, _Vp...> template<typename _CTp, typename _Rp>
: public __expanded_common_type_wrapper<typename __member_type_wrapper< struct __common_type_fold<_CTp, _Rp, void>
common_type<_Tp, _Up>>::type, _Vp...>::type
{ }; { };
template<typename _Tp, bool = is_enum<_Tp>::value> template<typename _Tp, bool = is_enum<_Tp>::value>
...@@ -2446,9 +2465,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION ...@@ -2446,9 +2465,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<bool _Cond, typename _Tp = void> template<bool _Cond, typename _Tp = void>
using __enable_if_t = typename enable_if<_Cond, _Tp>::type; 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 #if __cplusplus >= 201703L || !defined(__STRICT_ANSI__) // c++17 or gnu++11
#define __cpp_lib_void_t 201411 #define __cpp_lib_void_t 201411
/// A metafunction that always yields void, used for detecting valid types. /// A metafunction that always yields void, used for detecting valid types.
......
...@@ -214,6 +214,7 @@ static_assert(is_type<std::common_type<void(&&)(), void(&)()>, ...@@ -214,6 +214,7 @@ static_assert(is_type<std::common_type<void(&&)(), void(&)()>,
static_assert(is_type<std::common_type<void(&&)(), void(&&)()>, static_assert(is_type<std::common_type<void(&&)(), void(&&)()>,
void(*)()>(), ""); void(*)()>(), "");
static_assert(is_type<std::common_type<ImplicitTo<int>, int>, int>(), ""); 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>>, static_assert(is_type<std::common_type<ImplicitTo<int>, ImplicitTo<int>>,
ImplicitTo<int>>(), ""); ImplicitTo<int>>(), "");
static_assert(is_type<std::common_type<ImplicitTo<int>, 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&&>, ...@@ -260,19 +261,22 @@ static_assert(is_type<std::common_type<volatile Ukn&&, volatile Ukn&&>,
Ukn>(), ""); Ukn>(), "");
static_assert(is_type<std::common_type<X1, X2>, RX12>(), ""); 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<X2, X1>, RX21>(), "");
static_assert(is_type<std::common_type<X1, X2, X1>, Y1>(), ""); 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<X2, X1, X1>, Y3>(), "");
static_assert(is_type<std::common_type<X1, X1, X2>, RX12>(), ""); 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(is_type<std::common_type<X1, X1, X2, X1>, Y1>(), "");
static_assert(!has_type<std::common_type<>>(), ""); static_assert(!has_type<std::common_type<>>(), "");
static_assert(!has_type<std::common_type<int, S>>(), ""); 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, S>>(), "");
static_assert(!has_type<std::common_type<U, U2>>(), ""); 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<PrivateImplicitTo<int>, int>>(), "");
static_assert(!has_type<std::common_type<const PrivateImplicitTo<int>, static_assert(!has_type<std::common_type<const PrivateImplicitTo<int>,
int>>(), ""); int>>(), "");
......
...@@ -26,6 +26,6 @@ test01() ...@@ -26,6 +26,6 @@ test01()
// std::numeric_limits<int64_t>::max() == 9223372036854775807; // std::numeric_limits<int64_t>::max() == 9223372036854775807;
auto h = 9223372036854775808h; 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 // { dg-prune-output "in .constexpr. expansion" } // needed for -O0
...@@ -29,4 +29,4 @@ void test01() ...@@ -29,4 +29,4 @@ void test01()
test_type d; // { dg-error "required from here" } 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 }
...@@ -30,5 +30,5 @@ void test01() ...@@ -30,5 +30,5 @@ void test01()
test_type d; // { dg-error "required from here" } 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" } // { dg-prune-output "not a member" }
...@@ -31,4 +31,4 @@ void test01() ...@@ -31,4 +31,4 @@ void test01()
test_type d; // { dg-error "required from here" } test_type d; // { dg-error "required from here" }
} }
// { dg-error "period must be positive" "" { target *-*-* } 319 } // { dg-error "period must be positive" "" { target *-*-* } 0 }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment