diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -2411,6 +2411,196 @@ template using common_type_t = typename common_type<_Tp...>::type; #endif +// common_reference +#if _LIBCPP_STD_VER > 17 && defined(__cpp_concepts) && __cpp_concepts >= 201811L +// Let COND_RES(X, Y) be: +template +using __cond_res = + decltype(false ? _VSTD::declval<_Xp(&)()>()() : _VSTD::declval<_Yp(&)()>()()); + +// Let COPYCV(FROM, TO) be an alias for type TO with the addition of FROM’s +// top-level cv-qualifiers. +template +struct __copy_cv_ +{ + template + using __res_type = _To; +}; +template +struct __copy_cv_ +{ + template + using __res_type = add_const_t<_To>; +}; +template +struct __copy_cv_ +{ + template + using __res_type = add_volatile_t<_To>; +}; +template +struct __copy_cv_ +{ + template + using __res_type = add_cv_t<_To>; +}; +template +using __copy_cv = typename __copy_cv_<_From>::template __res_type<_To>; + +// Let XREF(A) denote a unary class [sic] template T such [...] +// [Note: XREF(A) is __xref::template __res_type] +template +struct __xref +{ + template + using __res_type = __copy_cv<_Tp, _Up>; +}; +template +struct __xref<_Tp&> +{ + template + using __res_type = add_lvalue_reference_t<__copy_cv<_Tp, _Up>>; +}; +template +struct __xref<_Tp&&> +{ + template + using __res_type = add_rvalue_reference_t<__copy_cv<_Tp, _Up>>; +}; + +// COMMON-REF(A, B) is COND-RES(COPYCV(X, Y) &, COPYCV(​Y, X) &) +// if that type exists and is a reference type. + +template, class _Yp = remove_reference_t<_Bp>> +struct __common_ref_ +{}; + +template +using __common_ref = typename __common_ref_<_Tp, _Up>::__type; + +template +requires (!is_reference_v<_Xp> && !is_reference_v<_Yp>) +using __cv_cond_res = __cond_res<__copy_cv<_Xp, _Yp>&, __copy_cv<_Yp, _Xp>&>; + +template +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&&. If A and B are both +// rvalue reference types, C is well-formed, and ... +template +using __common_ref_C = remove_reference_t<__common_ref<_Tp&, _Up&>>&&; + +template +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 +using __common_ref_D = __common_ref; + + +template +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 +struct __common_ref_<_Ap&, _Bp&&, _Xp, _Yp> : __common_ref_<_Bp&&, _Ap&> {}; + +// Note C: For the common_reference trait applied to a parameter pack [...] + +// bullet 1 - sizeof...(T) == 0 +template +struct _LIBCPP_TEMPLATE_VIS common_reference {}; + +template +using common_reference_t = typename common_reference<_Types...>::type; + +// bullet 2 - sizeof...(T) == 1 +template +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp> +{ + using type = _Tp; +}; + +// bullet 3 - sizeof...(T) == 2 + +// sub-bullet 4 & 5 - [...] if common_type_t is well-formed [...] +// - Otherwise, there shall be no member type. +template +struct __common_reference4 : common_type<_Tp, _Up> +{}; + +// sub-bullet 3 - [...] if COND_RES(T1, T2) is well-formed [...] +template +requires requires { typename __cond_res<_Tp, _Up>; } +struct __common_reference4<_Tp, _Up> +{ + using type = __cond_res<_Tp, _Up>; +}; + +// sub-bullet 2 - [...] if basic_common_reference<[...]>::type is well-formed [...] +template class, template class> +struct _LIBCPP_TEMPLATE_VIS basic_common_reference +{}; + +template +using __basic_common_ref = typename basic_common_reference< + remove_cvref_t<_Tp>, remove_cvref_t<_Up>, + __xref<_Tp>::template __res_type, __xref<_Up>::template __res_type>::type; + +template +struct __common_reference3 : __common_reference4<_Tp, _Up> +{}; + +template +requires requires { typename __basic_common_ref<_Tp, _Up>; } +struct __common_reference3<_Tp, _Up> +{ + using type = __basic_common_ref<_Tp, _Up>; +}; + +// sub-bullet 1 - If T1 and T2 are reference types and COMMON_REF(T1, T2) is well-formed [...] +template +struct __common_reference2 : __common_reference3<_Tp, _Up> +{}; + +template +requires is_reference_v<_Tp> && is_reference_v<_Up> && requires { typename __common_ref<_Tp, _Up>; } +struct __common_reference2<_Tp, _Up> +{ + using type = __common_ref<_Tp, _Up>; +}; + +template +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up> + : __common_reference2<_Tp, _Up> +{}; + +// bullet 4 - sizeof...(T) > 2 +template +requires requires { typename common_reference_t<_Tp, _Up>; } +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up, _Vp, _Rest...> + : common_reference, _Vp, _Rest...> +{}; + +#endif // _LIBCPP_STD_VER > 17 && defined(__cpp_concepts) && __cpp_concepts >= 201811L + // is_assignable template 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 --- /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 + +using std::common_reference; +using std::common_reference_t; +using std::is_same_v; +using std::void_t; + +template +constexpr bool has_type = requires { + typename T::type; +}; + +// A slightly simplified variation of std::tuple +template +struct Tuple {}; + +template +struct Tuple_helper {}; +template +struct Tuple_helper...>, Tuple, + Tuple > { + using type = Tuple...>; +}; + +namespace std { +template class TQual, + template class UQual> +struct basic_common_reference< ::Tuple, ::Tuple, TQual, UQual> + : ::Tuple_helper...>, Tuple...> > {}; +} // namespace std + +struct X2 {}; +struct Y2 {}; +struct Z2 {}; + +namespace std { +template <> +struct common_type { + using type = Z2; +}; +template <> +struct common_type { + using type = Z2; +}; +} // namespace std + +// (6.1) +// -- If sizeof...(T) is zero, there shall be no member type. +static_assert(!has_type >); + +// (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, void>); +static_assert(is_same_v, int>); +static_assert(is_same_v, int&>); +static_assert(is_same_v, int&&>); +static_assert(is_same_v, int const>); +static_assert(is_same_v, int const&>); +static_assert(is_same_v, int const&&>); +static_assert(is_same_v, int volatile[]>); +static_assert( + is_same_v, int volatile (&)[]>); +static_assert( + is_same_v, int volatile(&&)[]>); +static_assert(is_same_v, void (&)()>); +static_assert(is_same_v, 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, B&>); +static_assert(is_same_v, B const&>); +static_assert(is_same_v, B const&>); +static_assert(is_same_v, B const&>); +static_assert(is_same_v, B&>); + +static_assert(is_same_v, B&&>); +static_assert(is_same_v, B const&&>); +static_assert(is_same_v, B const&&>); +static_assert(is_same_v, B const&>); +static_assert(is_same_v, B const&>); +static_assert(is_same_v, B const&>); + +static_assert(is_same_v, B const&>); +static_assert(is_same_v, B const&>); +static_assert(is_same_v, B const&>); + +static_assert( + is_same_v, + int const volatile&&>); + +static_assert(is_same_v, + int const volatile&>); + +static_assert( + is_same_v, int const (&)[10]>); + +static_assert( + is_same_v, + int const volatile (&)[10]>); + +// (6.3.2) +// -- Otherwise, if basic_common_reference, +// remove_cvref_t, XREF(T1), XREF(T2)>::type is well-formed, then the +// member typedef type denotes that type. +static_assert(is_same_v&, + Tuple >, + Tuple >); + +static_assert(is_same_v&, + const Tuple&>, + const volatile Tuple&>); + +// (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, void>); +static_assert(is_same_v, int>); +static_assert(is_same_v, int>); +static_assert(is_same_v, int>); +static_assert(is_same_v, int>); + +// tricky volatile reference case +static_assert(is_same_v, int>); +static_assert(is_same_v, int>); + +static_assert(is_same_v, int*>); + +// https://github.com/ericniebler/stl2/issues/338 +struct MyIntRef { + MyIntRef(int&); +}; +static_assert(is_same_v, MyIntRef>); + +// (6.3.4) +// -- Otherwise, if common_type_t 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, moveonly>); +static_assert( + is_same_v, moveonly>); +static_assert( + is_same_v, moveonly>); + +static_assert(is_same_v, Z2>); + +// (6.3.5) +// -- Otherwise, there shall be no member type. +static_assert(!has_type&, + const Tuple&> >); + +// (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. 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. +static_assert(is_same_v, int>); +static_assert(is_same_v, + int const volatile&>); +static_assert(is_same_v, float>); + +// (6.4.2) +// -- Otherwise, there shall be no member type. +static_assert(!has_type >); + +int main(int, char**) { return 0; }