diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits --- a/libcxx/include/type_traits +++ b/libcxx/include/type_traits @@ -2161,6 +2161,183 @@ template using common_type_t = typename common_type<_Tp...>::type; #endif +// common_ref +#if _LIBCPP_STD_VER > 17 +// Let COND_RES(X, Y) be: +template +using __cond_res = + decltype(false ? _VSTD::declval<_Tp(&)()>()() : _VSTD::declval<_Up(&)()>()()); + +// 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 &, __copy_cv<_Up, _Tp>&>, + class = enable_if_t>> +using __common_ref = _Result; + +// Otherwise, let C be remove_­reference_­t&&. +template +using __common_ref_C = remove_reference_t<__common_ref<_Tp, _Up>>&&; + +// Otherwise, let D be COMMON-REF(const X&, Y&). +template +using __common_ref_D = __common_ref; + +// 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 +struct __common_reference4<_Tp, _Up, void_t<__cond_res<_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 +struct __common_reference3<_Tp, _Up, void_t<__basic_common_ref<_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 +struct __common_reference2<_Tp&, _Up&, void_t<__common_ref<_Tp, _Up>>> +{ + using type = __common_ref<_Tp, _Up>; +}; + +template +struct __common_reference2<_Tp&&, _Up&&, enable_if_t< + is_convertible_v<_Tp&&, __common_ref_C<_Tp, _Up>> && + is_convertible_v<_Up&&, __common_ref_C<_Tp, _Up>>>> +{ + using type = __common_ref_C<_Tp, _Up>; +}; + +template +struct __common_reference2<_Tp&&, _Up&, enable_if_t< + is_convertible_v<_Tp&&, __common_ref>>> +{ + using type = __common_ref_D<_Tp, _Up>; +}; + +template +struct __common_reference2<_Tp&, _Up&&, enable_if_t< + is_convertible_v<_Up&&, __common_ref>>> +{ + using type = __common_ref_D; +}; + +template +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up> + : __common_reference2<_Tp, _Up> +{}; + +// bullet 4 - sizeof...(T) > 2 +template +struct __fold_common_reference +{}; +template +struct __fold_common_reference>, _Tp, _Up, _Types...> + : common_reference, _Types...> +{}; + +template +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up, _Vp, _Rest...> + : __fold_common_reference +{}; + +#endif // _LIBCPP_STD_VER > 17 + // 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.pass.cpp b/libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.pass.cpp @@ -0,0 +1,230 @@ +//===----------------------------------------------------------------------===// +// +// 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++98, c++03, c++11, c++14, c++17 + +// 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 is_trait = false; +template +constexpr bool is_trait> = true; + +// 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...>> + {}; +} + +struct X2 {}; +struct Y2 {}; +struct Z2 {}; + +namespace std +{ + template <> + struct common_type + { + using type = Z2; + }; + template <> + struct common_type + { + using type = Z2; + }; +} + +// (6.1) +// -- If sizeof...(T) is zero, there shall be no member type. +void test_bullet_one() { + static_assert(!is_trait>); +} + +// (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. +void test_bullet_two() { + 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. +void test_bullet_three_one() { + { + 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. +void test_bullet_three_two() { + 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. +void test_bullet_three_three() { + 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. +void test_bullet_three_four() { + { + 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. +void test_bullet_three_five() { + static_assert(!is_trait< + common_reference&, 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. +void test_bullet_four_one() { + 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. +void test_bullet_four_two() { + static_assert(!is_trait>); +} + +int main(int, char**) +{ + test_bullet_one(); + test_bullet_two(); + test_bullet_three_one(); + test_bullet_three_two(); + test_bullet_three_three(); + test_bullet_three_four(); + test_bullet_three_five(); + test_bullet_four_one(); + test_bullet_four_two(); + + return 0; +}