Implement std::common_reference for C++20
diff mbox series

Message ID 20190910163000.GA27251@redhat.com
State New
Headers show
Series
  • Implement std::common_reference for C++20
Related show

Commit Message

Jonathan Wakely Sept. 10, 2019, 4:30 p.m. UTC
* include/std/type_traits (__do_common_type_impl): Implement
	additional COND-RES(CREF(D1), CRED(D2)) condition for C++20.
	(basic_common_reference, common_reference, common_reference_t): Define
	for C++20.
	* testsuite/20_util/common_reference/requirements/alias_decl.cc: New
	test.
	* testsuite/20_util/common_reference/requirements/
	explicit_instantiation.cc: New test.
	* testsuite/20_util/common_reference/requirements/typedefs.cc: New
	test.

Tested x86_64-linux, committed to trunk.
commit e4c0b7eac1619211b2dfa249086e7b40861ba77e
Author: redi <redi@138bc75d-0d04-0410-961f-82ee72b054a4>
Date:   Tue Sep 10 16:28:27 2019 +0000

    Implement std::common_reference for C++20
    
            * include/std/type_traits (__do_common_type_impl): Implement
            additional COND-RES(CREF(D1), CRED(D2)) condition for C++20.
            (basic_common_reference, common_reference, common_reference_t): Define
            for C++20.
            * testsuite/20_util/common_reference/requirements/alias_decl.cc: New
            test.
            * testsuite/20_util/common_reference/requirements/
            explicit_instantiation.cc: New test.
            * testsuite/20_util/common_reference/requirements/typedefs.cc: New
            test.
    
    git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@275594 138bc75d-0d04-0410-961f-82ee72b054a4

Patch
diff mbox series

diff --git a/libstdc++-v3/include/std/type_traits b/libstdc++-v3/include/std/type_traits
index d2f53591e5a..dc8a019324d 100644
--- a/libstdc++-v3/include/std/type_traits
+++ b/libstdc++-v3/include/std/type_traits
@@ -2189,6 +2189,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct conditional<false, _Iftrue, _Iffalse>
     { typedef _Iffalse type; };
 
+  // __remove_cvref_t (std::remove_cvref_t for C++11).
+  template<typename _Tp>
+    using __remove_cvref_t
+     = typename remove_cv<typename remove_reference<_Tp>::type>::type;
+
   /// common_type
   template<typename... _Tp>
     struct common_type;
@@ -2201,12 +2206,26 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       using __cond_t
 	= decltype(true ? std::declval<_Tp>() : std::declval<_Up>());
 
+    // if decay_t<decltype(false ? declval<D1>() : declval<D2>())>
+    // denotes a valid type, let C denote that type.
     template<typename _Tp, typename _Up>
-      static __success_type<typename decay<__cond_t<_Tp, _Up>>::type>
+      static __success_type<__decay_t<__cond_t<_Tp, _Up>>>
       _S_test(int);
 
+#if __cplusplus > 201703L
+    // Otherwise, if COND-RES(CREF(D1), CREF(D2)) denotes a type,
+    // let C denote the type decay_t<COND-RES(CREF(D1), CREF(D2))>.
+    template<typename _Tp, typename _Up>
+      static __success_type<__remove_cvref_t<__cond_t<const _Tp&, const _Up&>>>
+      _S_test_2(int);
+#endif
+
     template<typename, typename>
       static __failure_type
+      _S_test_2(...);
+
+    template<typename _Tp, typename _Up>
+      static decltype(_S_test_2<_Tp, _Up>(0))
       _S_test(...);
   };
 
@@ -2304,11 +2323,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return __declval<_Tp>(0);
     }
 
-  // __remove_cvref_t (std::remove_cvref_t for C++11).
-  template<typename _Tp>
-    using __remove_cvref_t
-     = typename remove_cv<typename remove_reference<_Tp>::type>::type;
-
   /// result_of
   template<typename _Signature>
     class result_of;
@@ -3248,6 +3262,164 @@  template <typename _From, typename _To>
   { return __builtin_is_constant_evaluated(); }
 #endif
 
+  template<typename _From, typename _To>
+    using __copy_cv = typename __match_cv_qualifiers<_From, _To>::__type;
+
+  template<typename _Xp, typename _Yp>
+    using __cond_res
+      = decltype(false ? declval<_Xp(&)()>()() : declval<_Yp(&)()>()());
+
+  template<typename _Ap, typename _Bp, typename = void>
+    struct __common_ref_impl
+    { };
+
+  // [meta.trans.other], COMMON-REF(A, B)
+  template<typename _Ap, typename _Bp>
+    using __common_ref = typename __common_ref_impl<_Ap, _Bp>::type;
+
+  // If A and B are both lvalue reference types, ...
+  template<typename _Xp, typename _Yp>
+    struct __common_ref_impl<_Xp&, _Yp&,
+      __void_t<__cond_res<__copy_cv<_Xp, _Yp>&, __copy_cv<_Yp, _Xp>&>>>
+    { using type = __cond_res<__copy_cv<_Xp, _Yp>&, __copy_cv<_Yp, _Xp>&>; };
+
+  // let C be remove_reference_t<COMMON-REF(X&, Y&)>&&
+  template<typename _Xp, typename _Yp>
+    using __common_ref_C = remove_reference_t<__common_ref<_Xp&, _Yp&>>&&;
+
+  // If A and B are both rvalue reference types, ...
+  template<typename _Xp, typename _Yp>
+    struct __common_ref_impl<_Xp&&, _Yp&&,
+      _Require<is_convertible<_Xp&&, __common_ref_C<_Xp, _Yp>>,
+	       is_convertible<_Yp&&, __common_ref_C<_Xp, _Yp>>>>
+    { using type = __common_ref_C<_Xp, _Yp>; };
+
+  // let D be COMMON-REF(const X&, Y&)
+  template<typename _Xp, typename _Yp>
+    using __common_ref_D = __common_ref<const _Xp&, _Yp&>;
+
+  // If A is an rvalue reference and B is an lvalue reference, ...
+  template<typename _Xp, typename _Yp>
+    struct __common_ref_impl<_Xp&&, _Yp&,
+      _Require<is_convertible<_Xp&&, __common_ref_D<_Xp, _Yp>>>>
+    { using type = __common_ref_D<_Xp, _Yp>; };
+
+  // If A is an lvalue reference and B is an rvalue reference, ...
+  template<typename _Xp, typename _Yp>
+    struct __common_ref_impl<_Xp&, _Yp&&>
+    : __common_ref_impl<_Yp&&, _Xp&>
+    { };
+
+  template<typename _Tp, typename _Up,
+	   template<typename> class _TQual, template<typename> class _UQual>
+    struct basic_common_reference
+    { };
+
+  template<typename _Tp>
+    struct __xref
+    { template<typename _Up> using __type = __copy_cv<_Tp, _Up>; };
+
+  template<typename _Tp>
+    struct __xref<_Tp&>
+    { template<typename _Up> using __type = __copy_cv<_Tp, _Up>&; };
+
+  template<typename _Tp>
+    struct __xref<_Tp&&>
+    { template<typename _Up> using __type = __copy_cv<_Tp, _Up>&&; };
+
+  template<typename _Tp1, typename _Tp2>
+    using __basic_common_ref
+      = typename basic_common_reference<remove_cvref_t<_Tp1>,
+					remove_cvref_t<_Tp2>,
+					__xref<_Tp1>::template __type,
+					__xref<_Tp2>::template __type>::type;
+
+  template<typename... _Tp>
+    struct common_reference;
+
+  template<typename... _Tp>
+    using common_reference_t = typename common_reference<_Tp...>::type;
+
+  // If sizeof...(T) is zero, there shall be no member type.
+  template<>
+    struct common_reference<>
+    { };
+
+  // If sizeof...(T) is one ...
+  template<typename _Tp0>
+    struct common_reference<_Tp0>
+    { using type = _Tp0; };
+
+  template<typename _Tp1, typename _Tp2, int _Bullet = 1, typename = void>
+    struct __common_reference_impl
+    : __common_reference_impl<_Tp1, _Tp2, _Bullet + 1>
+    { };
+
+  // If sizeof...(T) is two ...
+  template<typename _Tp1, typename _Tp2>
+    struct common_reference<_Tp1, _Tp2>
+    : __common_reference_impl<_Tp1, _Tp2>
+    { };
+
+  // If T1 and T2 are reference types and COMMON-REF(T1, T2) is well-formed, ...
+  template<typename _Tp1, typename _Tp2>
+    struct __common_reference_impl<_Tp1&, _Tp2&, 1,
+				   void_t<__common_ref<_Tp1&, _Tp2&>>>
+    { using type = __common_ref<_Tp1&, _Tp2&>; };
+
+  template<typename _Tp1, typename _Tp2>
+    struct __common_reference_impl<_Tp1&&, _Tp2&&, 1,
+				   void_t<__common_ref<_Tp1&&, _Tp2&&>>>
+    { using type = __common_ref<_Tp1&&, _Tp2&&>; };
+
+  template<typename _Tp1, typename _Tp2>
+    struct __common_reference_impl<_Tp1&, _Tp2&&, 1,
+				   void_t<__common_ref<_Tp1&, _Tp2&&>>>
+    { using type = __common_ref<_Tp1&, _Tp2&&>; };
+
+  template<typename _Tp1, typename _Tp2>
+    struct __common_reference_impl<_Tp1&&, _Tp2&, 1,
+				   void_t<__common_ref<_Tp1&&, _Tp2&>>>
+    { using type = __common_ref<_Tp1&&, _Tp2&>; };
+
+  // Otherwise, if basic_common_reference<...>::type is well-formed, ...
+  template<typename _Tp1, typename _Tp2>
+    struct __common_reference_impl<_Tp1, _Tp2, 2,
+				   void_t<__basic_common_ref<_Tp1, _Tp2>>>
+    { using type = __basic_common_ref<_Tp1, _Tp2>; };
+
+  // Otherwise, if COND-RES(T1, T2) is well-formed, ...
+  template<typename _Tp1, typename _Tp2>
+    struct __common_reference_impl<_Tp1, _Tp2, 3,
+				   void_t<__cond_res<_Tp1, _Tp2>>>
+    { using type = __cond_res<_Tp1, _Tp2>; };
+
+  // Otherwise, if common_type_t<T1, T2> is well-formed, ...
+  template<typename _Tp1, typename _Tp2>
+    struct __common_reference_impl<_Tp1, _Tp2, 4,
+				   void_t<common_type_t<_Tp1, _Tp2>>>
+    { using type = common_type_t<_Tp1, _Tp2>; };
+
+  // Otherwise, there shall be no member type.
+  template<typename _Tp1, typename _Tp2>
+    struct __common_reference_impl<_Tp1, _Tp2, 5, void>
+    { };
+
+  // Otherwise, if sizeof...(T) is greater than two, ...
+  template<typename _Tp1, typename _Tp2, typename... _Rest>
+    struct common_reference<_Tp1, _Tp2, _Rest...>
+    : __common_type_fold<common_reference<_Tp1, _Tp2>,
+			 __common_type_pack<_Rest...>>
+    { };
+
+  // Reuse __common_type_fold for common_reference<T1, T2, Rest...>
+  template<typename _Tp1, typename _Tp2, typename... _Rest>
+    struct __common_type_fold<common_reference<_Tp1, _Tp2>,
+			      __common_type_pack<_Rest...>,
+			      void_t<common_reference_t<_Tp1, _Tp2>>>
+    : public common_reference<common_reference_t<_Tp1, _Tp2>, _Rest...>
+    { };
+
 #endif // C++2a
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/20_util/common_reference/requirements/alias_decl.cc b/libstdc++-v3/testsuite/20_util/common_reference/requirements/alias_decl.cc
new file mode 100644
index 00000000000..2c318405354
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/common_reference/requirements/alias_decl.cc
@@ -0,0 +1,30 @@ 
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <type_traits>
+
+using namespace std;
+
+static_assert( is_same_v<common_reference<int>::type,
+			 common_reference_t<int>>);
+static_assert( is_same_v<common_reference<int&, const int&>::type,
+			 common_reference_t<int&, const int&>>);
+static_assert( is_same_v<common_reference<int, long&, char, unsigned&>::type,
+			 common_reference_t<int, long&, char, unsigned&>>);
diff --git a/libstdc++-v3/testsuite/20_util/common_reference/requirements/explicit_instantiation.cc b/libstdc++-v3/testsuite/20_util/common_reference/requirements/explicit_instantiation.cc
new file mode 100644
index 00000000000..6b0337741ca
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/common_reference/requirements/explicit_instantiation.cc
@@ -0,0 +1,42 @@ 
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// NB: This file is for testing type_traits with NO OTHER INCLUDES.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <type_traits>
+
+using test_type1 = int;
+using test_type2 = int&;
+using test_type3 = double;
+using test_type4 = float&;
+using test_type5 = void;
+using test_type6 = const void;
+
+namespace std
+{
+  template struct common_reference<>;
+  template struct common_reference<test_type1>;
+  template struct common_reference<test_type1, test_type2>;
+  template struct common_reference<test_type1, test_type2, test_type3>;
+  template struct common_reference<test_type1, test_type2, test_type3, test_type4>;
+
+  template struct common_reference<test_type5>;
+  template struct common_reference<test_type5, test_type6>;
+}
diff --git a/libstdc++-v3/testsuite/20_util/common_reference/requirements/typedefs.cc b/libstdc++-v3/testsuite/20_util/common_reference/requirements/typedefs.cc
new file mode 100644
index 00000000000..e0aec8b6cda
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/common_reference/requirements/typedefs.cc
@@ -0,0 +1,92 @@ 
+// Copyright (C) 2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-std=gnu++2a" }
+// { dg-do compile { target c++2a } }
+
+#include <type_traits>
+
+template<typename T, typename = void>
+  struct has_type : std::false_type { };
+
+template<typename T>
+  struct has_type<T, std::void_t<typename T::type>> : std::true_type { };
+
+template<typename... T>
+constexpr bool
+has_common_ref()
+{
+  return has_type<std::common_reference<T...>>::value;
+}
+
+using std::is_same_v;
+using std::common_reference_t;
+
+void test01()
+{
+
+  static_assert( !has_common_ref<>() );
+  static_assert( !has_common_ref<char(*)(), int(*)()>() );
+  static_assert( !has_common_ref<void*, int>() );
+
+  static_assert( is_same_v<common_reference_t<int>, int> );
+  static_assert( is_same_v<common_reference_t<int&>, int&> );
+  static_assert( is_same_v<common_reference_t<void>, void> );
+  static_assert( is_same_v<common_reference_t<const void>, const void> );
+  static_assert( is_same_v<common_reference_t<const void, void>, void> );
+  static_assert( is_same_v<common_reference_t<void(*const)(), void(*)()>, void(*)()> );
+  static_assert( is_same_v<common_reference_t<int, int>, int> );
+  static_assert( is_same_v<common_reference_t<int&, int>, int> );
+  static_assert( is_same_v<common_reference_t<int, int&>, int> );
+  static_assert( is_same_v<common_reference_t<int&&, int>, int> );
+  static_assert( is_same_v<common_reference_t<int&, int&>, int&> );
+  static_assert( is_same_v<common_reference_t<int&, int&&>, const int&> );
+  static_assert( is_same_v<common_reference_t<int&&, int&>, const int&> );
+  static_assert( is_same_v<common_reference_t<int&&, int&&>, int&&> );
+  static_assert( is_same_v<common_reference_t<int&&, const int&&>, const int&&> );
+  static_assert( is_same_v<common_reference_t<int&, int&, int&&>, const int&> );
+  static_assert( is_same_v<common_reference_t<int&&, int&, int&>, const int&> );
+  static_assert( is_same_v<common_reference_t<char&, int&>, int> );
+  static_assert( is_same_v<common_reference_t<long&, int&>, long> );
+}
+
+struct A { };
+struct B { };
+struct C { };
+
+template<template<typename> class AQual, template<typename> class BQual>
+struct std::basic_common_reference<A, B, AQual, BQual>
+{
+  using type = BQual<AQual<C>>;
+};
+
+static_assert( is_same_v<common_reference_t<A, B>, C> );
+static_assert( is_same_v<common_reference_t<A&, B>, C&> );
+static_assert( is_same_v<common_reference_t<A&, const B>, C&> );
+static_assert( is_same_v<common_reference_t<const A, B&>, const C&> );
+static_assert( is_same_v<common_reference_t<const A&, B&&>, const C&> );
+static_assert( is_same_v<common_reference_t<const A, B&&>, const C&&> );
+
+struct D { };
+struct E { };
+struct F { };
+
+template<> struct std::common_type<D, E> { using type = F; };
+
+static_assert( is_same_v<common_reference_t<D, E>, F> );
+static_assert( is_same_v<common_reference_t<D&, E>, F> );
+static_assert( is_same_v<common_reference_t<D&, E&&>, F> );