[libcxx-commits] [libcxx] eadece3 - [libcxx] adds common_reference to <type_traits>

Christopher Di Bella via libcxx-commits libcxx-commits at lists.llvm.org
Tue Mar 2 14:37:57 PST 2021


Author: Christopher Di Bella
Date: 2021-03-02T22:33:37Z
New Revision: eadece333f6112502046fad0f2c062d13a37079b

URL: https://github.com/llvm/llvm-project/commit/eadece333f6112502046fad0f2c062d13a37079b
DIFF: https://github.com/llvm/llvm-project/commit/eadece333f6112502046fad0f2c062d13a37079b.diff

LOG: [libcxx] adds common_reference to <type_traits>

Implements part of P0898R3 Standard Library Concepts

Reworks D74351 to use requires-clauses over SFINAE and so that it more
closely follows the wording.

Co-authored by: Michael Schellenberger Costa <mschellenbergercosta at googlemail.com>

(Michael did all the heavy lifting and I came in to polish it for
 submission, since Michael is focussing on `std::format` now.)

Reviewed By: ldionne, #libc

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

Added: 
    libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.compile.pass.cpp

Modified: 
    libcxx/include/type_traits

Removed: 
    


################################################################################
diff  --git a/libcxx/include/type_traits b/libcxx/include/type_traits
index 59dfd1e9bae6..cc88e38e497f 100644
--- a/libcxx/include/type_traits
+++ b/libcxx/include/type_traits
@@ -2411,6 +2411,216 @@ struct _LIBCPP_TEMPLATE_VIS
 template <class ..._Tp> using common_type_t = typename common_type<_Tp...>::type;
 #endif
 
+#if _LIBCPP_STD_VER > 11
+// Let COPYCV(FROM, TO) be an alias for type TO with the addition of FROM’s
+// top-level cv-qualifiers.
+template <class _From, class _To>
+struct __copy_cv
+{
+    using type = _To;
+};
+
+template <class _From, class _To>
+struct __copy_cv<const _From, _To>
+{
+    using type = add_const_t<_To>;
+};
+
+template <class _From, class _To>
+struct __copy_cv<volatile _From, _To>
+{
+    using type = add_volatile_t<_To>;
+};
+
+template <class _From, class _To>
+struct __copy_cv<const volatile _From, _To>
+{
+    using type = add_cv_t<_To>;
+};
+
+template <class _From, class _To>
+using __copy_cv_t = typename __copy_cv<_From, _To>::type;
+
+template <class _From, class _To>
+struct __copy_cvref
+{
+    using type = __copy_cv_t<_From, _To>;
+};
+
+template <class _From, class _To>
+struct __copy_cvref<_From&, _To>
+{
+    using type = add_lvalue_reference_t<__copy_cv_t<_From, _To>>;
+};
+
+template <class _From, class _To>
+struct __copy_cvref<_From&&, _To>
+{
+    using type = add_rvalue_reference_t<__copy_cv_t<_From, _To>>;
+};
+
+template <class _From, class _To>
+using __copy_cvref_t = typename __copy_cvref<_From, _To>::type;
+
+#endif // _LIBCPP_STD_VER > 11
+
+// common_reference
+#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+// Let COND_RES(X, Y) be:
+template <class _Xp, class _Yp>
+using __cond_res =
+    decltype(false ? _VSTD::declval<_Xp(&)()>()() : _VSTD::declval<_Yp(&)()>()());
+
+// Let `XREF(A)` denote a unary alias template `T` such that `T<U>` denotes the same type as `U`
+// with the addition of `A`'s cv and reference qualifiers, for a non-reference cv-unqualified type
+// `U`.
+// [Note: `XREF(A)` is `__xref<A>::template __apply`]
+template <class _Tp>
+struct __xref {
+  template<class _Up>
+  using __apply = __copy_cvref_t<_Tp, _Up>;
+};
+
+// Given types `A` and `B`, let `X` be `remove_­reference_­t<A>`, let `Y` be `remove_­reference_­t<B>`,
+// and let `COMMON-​REF(A, B)` be:
+template<class _Ap, class _Bp, class _Xp = remove_reference_t<_Ap>, class _Yp = remove_reference_t<_Bp>>
+struct __common_ref;
+
+template<class _Xp, class _Yp>
+using __common_ref_t = typename __common_ref<_Xp, _Yp>::__type;
+
+template<class _Xp, class _Yp>
+using __cv_cond_res = __cond_res<__copy_cv_t<_Xp, _Yp>&, __copy_cv_t<_Yp, _Xp>&>;
+
+
+//    If `A` and `B` are both lvalue reference types, `COMMON-REF(A, B)` is
+//    `COND-RES(COPYCV(X, Y) &, COPYCV(​Y, X) &)` if that type exists and is a reference type.
+template<class _Ap, class _Bp, class _Xp, class _Yp>
+requires requires { typename __cv_cond_res<_Xp, _Yp>; } && is_reference_v<__cv_cond_res<_Xp, _Yp>>
+struct __common_ref<_Ap&, _Bp&, _Xp, _Yp>
+{
+    using __type = __cv_cond_res<_Xp, _Yp>;
+};
+
+//    Otherwise, let `C` be `remove_­reference_­t<COMMON-REF(X&, Y&)>&&`....
+template <class _Xp, class _Yp>
+using __common_ref_C = remove_reference_t<__common_ref_t<_Xp&, _Yp&>>&&;
+
+
+//    .... If `A` and `B` are both rvalue reference types, `C` is well-formed, and
+//    `is_­convertible_­v<A, C> && is_­convertible_­v<B, C>` is `true`, then `COMMON-REF(A, B)` is `C`.
+template<class _Ap, class _Bp, class _Xp, class _Yp>
+requires
+  requires { typename __common_ref_C<_Xp, _Yp>; } &&
+  is_convertible_v<_Ap&&, __common_ref_C<_Xp, _Yp>> &&
+  is_convertible_v<_Bp&&, __common_ref_C<_Xp, _Yp>>
+struct __common_ref<_Ap&&, _Bp&&, _Xp, _Yp>
+{
+    using __type = __common_ref_C<_Xp, _Yp>;
+};
+
+//    Otherwise, let `D` be `COMMON-REF(const X&, Y&)`....
+template <class _Tp, class _Up>
+using __common_ref_D = __common_ref_t<const _Tp&, _Up&>;
+
+//    ... If `A` is an rvalue reference and `B` is an lvalue reference and `D` is well-formed and
+//    `is_­convertible_­v<A, D>` is `true`, then `COMMON-REF(A, B)` is `D`.
+template<class _Ap, class _Bp, class _Xp, class _Yp>
+requires requires { typename __common_ref_D<_Xp, _Yp>; } &&
+         is_convertible_v<_Ap&&, __common_ref_D<_Xp, _Yp>>
+struct __common_ref<_Ap&&, _Bp&, _Xp, _Yp>
+{
+    using __type = __common_ref_D<_Xp, _Yp>;
+};
+
+//    Otherwise, if `A` is an lvalue reference and `B` is an rvalue reference, then
+//    `COMMON-REF(A, B)` is `COMMON-REF(B, A)`.
+template<class _Ap, class _Bp, class _Xp, class _Yp>
+struct __common_ref<_Ap&, _Bp&&, _Xp, _Yp> : __common_ref<_Bp&&, _Ap&> {};
+
+//    Otherwise, `COMMON-REF(A, B)` is ill-formed.
+template<class _Ap, class _Bp, class _Xp, class _Yp>
+struct __common_ref {};
+
+// Note C: For the common_reference trait applied to a parameter pack [...]
+
+template <class...>
+struct common_reference;
+
+template <class... _Types>
+using common_reference_t = typename common_reference<_Types...>::type;
+
+// bullet 1 - sizeof...(T) == 0
+template<>
+struct common_reference<> {};
+
+// bullet 2 - sizeof...(T) == 1
+template <class _Tp>
+struct common_reference<_Tp>
+{
+    using type = _Tp;
+};
+
+// bullet 3 - sizeof...(T) == 2
+template <class _Tp, class _Up> struct __common_reference_sub_bullet3;
+template <class _Tp, class _Up> struct __common_reference_sub_bullet2 : __common_reference_sub_bullet3<_Tp, _Up> {};
+template <class _Tp, class _Up> struct __common_reference_sub_bullet1 : __common_reference_sub_bullet2<_Tp, _Up> {};
+
+// sub-bullet 1 - If `T1` and `T2` are reference types and `COMMON-REF(T1, T2)` is well-formed, then
+// the member typedef type denotes that type.
+template <class _Tp, class _Up> struct common_reference<_Tp, _Up> : __common_reference_sub_bullet1<_Tp, _Up> {};
+
+template <class _Tp, class _Up>
+requires is_reference_v<_Tp> && is_reference_v<_Up> && requires { typename __common_ref_t<_Tp, _Up>; }
+struct __common_reference_sub_bullet1<_Tp, _Up>
+{
+    using type = __common_ref_t<_Tp, _Up>;
+};
+
+// sub-bullet 2 - Otherwise, if `basic_­common_­reference<remove_­cvref_­t<T1>, remove_­cvref_­t<T2>, ​XREF(​T1), XREF(T2)>​::​type`
+// is well-formed, then the member typedef type denotes that type.
+template <class, class, template <class> class, template <class> class> struct basic_common_reference {};
+
+template <class _Tp, class _Up>
+using __basic_common_reference_t = typename basic_common_reference<
+    remove_cvref_t<_Tp>, remove_cvref_t<_Up>,
+    __xref<_Tp>::template __apply, __xref<_Up>::template __apply>::type;
+
+template <class _Tp, class _Up>
+requires requires { typename __basic_common_reference_t<_Tp, _Up>; }
+struct __common_reference_sub_bullet2<_Tp, _Up>
+{
+    using type = __basic_common_reference_t<_Tp, _Up>;
+};
+
+// sub-bullet 3 - Otherwise, if `COND-RES(T1, T2)` is well-formed, then the member typedef type
+// denotes that type.
+template <class _Tp, class _Up>
+requires requires { typename __cond_res<_Tp, _Up>; }
+struct __common_reference_sub_bullet3<_Tp, _Up>
+{
+    using type = __cond_res<_Tp, _Up>;
+};
+
+
+// sub-bullet 4 & 5 - Otherwise, if `common_­type_­t<T1, T2>` is well-formed, then the member typedef
+//                    type denotes that type.
+//                  - Otherwise, there shall be no member type.
+template <class _Tp, class _Up> struct __common_reference_sub_bullet3 : common_type<_Tp, _Up> {};
+
+// bullet 4 - If there is such a type `C`, the member typedef type shall denote the same type, if
+//            any, as `common_­reference_­t<C, Rest...>`.
+template <class _Tp, class _Up, class _Vp, class... _Rest>
+requires requires { typename common_reference_t<_Tp, _Up>; }
+struct common_reference<_Tp, _Up, _Vp, _Rest...>
+    : common_reference<common_reference_t<_Tp, _Up>, _Vp, _Rest...>
+{};
+
+// bullet 5 - Otherwise, there shall be no member type.
+template <class...> struct common_reference {};
+
+#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS)
+
 // is_assignable
 
 template<typename, typename _Tp> struct __select_2nd { typedef _LIBCPP_NODEBUG_TYPE _Tp type; };

diff  --git a/libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.compile.pass.cpp b/libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.compile.pass.cpp
new file mode 100644
index 000000000000..7e2c34b09e0c
--- /dev/null
+++ b/libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.compile.pass.cpp
@@ -0,0 +1,196 @@
+//===----------------------------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+// UNSUPPORTED: libcpp-no-concepts
+
+// type_traits
+// common_reference
+
+#include <type_traits>
+
+using std::common_reference;
+using std::common_reference_t;
+using std::is_same_v;
+using std::void_t;
+
+template <class T>
+constexpr bool has_type = requires {
+  typename T::type;
+};
+
+// A slightly simplified variation of std::tuple
+template <class...>
+struct Tuple {};
+
+template <class, class, class>
+struct Tuple_helper {};
+template <class... Ts, class... Us>
+struct Tuple_helper<void_t<common_reference_t<Ts, Us>...>, Tuple<Ts...>,
+                    Tuple<Us...> > {
+  using type = Tuple<common_reference_t<Ts, Us>...>;
+};
+
+namespace std {
+template <class... Ts, class... Us, template <class> class TQual,
+          template <class> class UQual>
+struct basic_common_reference< ::Tuple<Ts...>, ::Tuple<Us...>, TQual, UQual>
+    : ::Tuple_helper<void, Tuple<TQual<Ts>...>, Tuple<UQual<Us>...> > {};
+} // namespace std
+
+struct X2 {};
+struct Y2 {};
+struct Z2 {};
+
+namespace std {
+template <>
+struct common_type<X2, Y2> {
+  using type = Z2;
+};
+template <>
+struct common_type<Y2, X2> {
+  using type = Z2;
+};
+} // namespace std
+
+// (6.1)
+//  -- If sizeof...(T) is zero, there shall be no member type.
+static_assert(!has_type<common_reference<> >);
+
+// (6.2)
+//  -- Otherwise, if sizeof...(T) is one, let T0 denote the sole type in the
+//     pack T. The member typedef type shall denote the same type as T0.
+static_assert(is_same_v<common_reference_t<void>, void>);
+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<int&&>, int&&>);
+static_assert(is_same_v<common_reference_t<int const>, int const>);
+static_assert(is_same_v<common_reference_t<int const&>, int const&>);
+static_assert(is_same_v<common_reference_t<int const&&>, int const&&>);
+static_assert(is_same_v<common_reference_t<int volatile[]>, int volatile[]>);
+static_assert(
+    is_same_v<common_reference_t<int volatile (&)[]>, int volatile (&)[]>);
+static_assert(
+    is_same_v<common_reference_t<int volatile(&&)[]>, int volatile(&&)[]>);
+static_assert(is_same_v<common_reference_t<void (&)()>, void (&)()>);
+static_assert(is_same_v<common_reference_t<void(&&)()>, void(&&)()>);
+
+// (6.3)
+//  -- Otherwise, if sizeof...(T) is two, let T1 and T2 denote the two types in
+//     the pack T. Then
+// (6.3.1)
+//    -- If T1 and T2 are reference types and COMMON_REF(T1, T2) is well-formed,
+//       then the member typedef type denotes that type.
+struct B {};
+struct D : B {};
+static_assert(is_same_v<common_reference_t<B&, D&>, B&>);
+static_assert(is_same_v<common_reference_t<B const&, D&>, B const&>);
+static_assert(is_same_v<common_reference_t<B&, D const&>, B const&>);
+static_assert(is_same_v<common_reference_t<B&, D const&, D&>, B const&>);
+static_assert(is_same_v<common_reference_t<B&, D&, B&, D&>, B&>);
+
+static_assert(is_same_v<common_reference_t<B&&, D&&>, B&&>);
+static_assert(is_same_v<common_reference_t<B const&&, D&&>, B const&&>);
+static_assert(is_same_v<common_reference_t<B&&, D const&&>, B const&&>);
+static_assert(is_same_v<common_reference_t<B&, D&&>, B const&>);
+static_assert(is_same_v<common_reference_t<B&, D const&&>, B const&>);
+static_assert(is_same_v<common_reference_t<B const&, D&&>, B const&>);
+
+static_assert(is_same_v<common_reference_t<B&&, D&>, B const&>);
+static_assert(is_same_v<common_reference_t<B&&, D const&>, B const&>);
+static_assert(is_same_v<common_reference_t<B const&&, D&>, B const&>);
+
+static_assert(
+    is_same_v<common_reference_t<int const volatile&&, int volatile&&>,
+              int const volatile&&>);
+
+static_assert(is_same_v<common_reference_t<int const&, int volatile&>,
+                        int const volatile&>);
+
+static_assert(
+    is_same_v<common_reference_t<int (&)[10], int(&&)[10]>, int const (&)[10]>);
+
+static_assert(
+    is_same_v<common_reference_t<int const (&)[10], int volatile (&)[10]>,
+              int const volatile (&)[10]>);
+
+// (6.3.2)
+//    -- Otherwise, if basic_common_reference<remove_cvref_t<T1>,
+//       remove_cvref_t<T2>, XREF(T1), XREF(T2)>::type is well-formed, then the
+//       member typedef type denotes that type.
+static_assert(is_same_v<common_reference_t<const Tuple<int, short>&,
+                                           Tuple<int&, short volatile&> >,
+                        Tuple<const int&, const volatile short&> >);
+
+static_assert(is_same_v<common_reference_t<volatile Tuple<int, short>&,
+                                           const Tuple<int, short>&>,
+                        const volatile Tuple<int, short>&>);
+
+// (6.3.3)
+//    -- Otherwise, if COND_RES(T1, T2) is well-formed, then the member typedef
+//       type denotes that type.
+static_assert(is_same_v<common_reference_t<void, void>, void>);
+static_assert(is_same_v<common_reference_t<int, short>, int>);
+static_assert(is_same_v<common_reference_t<int, short&>, int>);
+static_assert(is_same_v<common_reference_t<int&, short&>, int>);
+static_assert(is_same_v<common_reference_t<int&, short>, int>);
+
+// tricky volatile reference case
+static_assert(is_same_v<common_reference_t<int&&, int volatile&>, int>);
+static_assert(is_same_v<common_reference_t<int volatile&, int&&>, int>);
+
+static_assert(is_same_v<common_reference_t<int (&)[10], int (&)[11]>, int*>);
+
+// https://github.com/ericniebler/stl2/issues/338
+struct MyIntRef {
+  MyIntRef(int&);
+};
+static_assert(is_same_v<common_reference_t<int&, MyIntRef>, MyIntRef>);
+
+// (6.3.4)
+//    -- Otherwise, if common_type_t<T1, T2> is well-formed, then the member
+//       typedef type denotes that type.
+struct moveonly {
+  moveonly() = default;
+  moveonly(moveonly&&) = default;
+  moveonly& operator=(moveonly&&) = default;
+};
+struct moveonly2 : moveonly {};
+
+static_assert(
+    is_same_v<common_reference_t<moveonly const&, moveonly>, moveonly>);
+static_assert(
+    is_same_v<common_reference_t<moveonly2 const&, moveonly>, moveonly>);
+static_assert(
+    is_same_v<common_reference_t<moveonly const&, moveonly2>, moveonly>);
+
+static_assert(is_same_v<common_reference_t<X2&, Y2 const&>, Z2>);
+
+// (6.3.5)
+//    -- Otherwise, there shall be no member type.
+static_assert(!has_type<common_reference<volatile Tuple<short>&,
+                                         const Tuple<int, short>&> >);
+
+// (6.4)
+//  -- Otherwise, if sizeof...(T) is greater than two, let T1, T2, and Rest,
+//     respectively, denote the first, second, and (pack of) remaining types
+//     comprising T. Let C be the type common_reference_t<T1, T2>. Then:
+// (6.4.1)
+//    -- If there is such a type C, the member typedef type shall denote the
+//       same type, if any, as common_reference_t<C, Rest...>.
+static_assert(is_same_v<common_reference_t<int, int, int>, int>);
+static_assert(is_same_v<common_reference_t<int&&, int const&, int volatile&>,
+                        int const volatile&>);
+static_assert(is_same_v<common_reference_t<int&&, int const&, float&>, float>);
+
+// (6.4.2)
+//    -- Otherwise, there shall be no member type.
+static_assert(!has_type<common_reference<int, short, int, char*> >);
+
+int main(int, char**) { return 0; }


        


More information about the libcxx-commits mailing list