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,216 @@ template 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 +struct __copy_cv +{ + using type = _To; +}; + +template +struct __copy_cv +{ + using type = add_const_t<_To>; +}; + +template +struct __copy_cv +{ + using type = add_volatile_t<_To>; +}; + +template +struct __copy_cv +{ + using type = add_cv_t<_To>; +}; + +template +using __copy_cv_t = typename __copy_cv<_From, _To>::type; + +template +struct __copy_cvref +{ + using type = __copy_cv_t<_From, _To>; +}; + +template +struct __copy_cvref<_From&, _To> +{ + using type = add_lvalue_reference_t<__copy_cv_t<_From, _To>>; +}; + +template +struct __copy_cvref<_From&&, _To> +{ + using type = add_rvalue_reference_t<__copy_cv_t<_From, _To>>; +}; + +template +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 +using __cond_res = + decltype(false ? _VSTD::declval<_Xp(&)()>()() : _VSTD::declval<_Yp(&)()>()()); + +// Let `XREF(A)` denote a unary alias template `T` such that `T` 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::template __apply`] +template +struct __xref { + template + using __apply = __copy_cvref_t<_Tp, _Up>; +}; + +// Given types `A` and `B`, let `X` be `remove_­reference_­t`, let `Y` be `remove_­reference_­t`, +// and let `COMMON-​REF(A, B)` be: +template, class _Yp = remove_reference_t<_Bp>> +struct __common_ref; + +template +using __common_ref_t = typename __common_ref<_Xp, _Yp>::__type; + +template +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 +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&&`.... +template +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 && is_­convertible_­v` is `true`, then `COMMON-REF(A, B)` is `C`. +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_t; + +// ... If `A` is an rvalue reference and `B` is an lvalue reference and `D` is well-formed and +// `is_­convertible_­v` is `true`, then `COMMON-REF(A, B)` is `D`. +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&> {}; + +// Otherwise, `COMMON-REF(A, B)` is ill-formed. +template +struct __common_ref {}; + +// Note C: For the common_reference trait applied to a parameter pack [...] + +template +struct common_reference; + +template +using common_reference_t = typename common_reference<_Types...>::type; + +// bullet 1 - sizeof...(T) == 0 +template<> +struct common_reference<> {}; + +// bullet 2 - sizeof...(T) == 1 +template +struct common_reference<_Tp> +{ + using type = _Tp; +}; + +// bullet 3 - sizeof...(T) == 2 +template struct __common_reference_sub_bullet3; +template struct __common_reference_sub_bullet2 : __common_reference_sub_bullet3<_Tp, _Up> {}; +template 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 struct common_reference<_Tp, _Up> : __common_reference_sub_bullet1<_Tp, _Up> {}; + +template +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, ​XREF(​T1), XREF(T2)>​::​type` +// is well-formed, then the member typedef type denotes that type. +template class, template class> struct basic_common_reference {}; + +template +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 +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 +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` is well-formed, then the member typedef +// type denotes that type. +// - Otherwise, there shall be no member type. +template 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`. +template +requires requires { typename common_reference_t<_Tp, _Up>; } +struct common_reference<_Tp, _Up, _Vp, _Rest...> + : common_reference, _Vp, _Rest...> +{}; + +// bullet 5 - Otherwise, there shall be no member type. +template struct common_reference {}; + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_CONCEPTS) + // 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; }