CaseyCarter created this revision. CaseyCarter added reviewers: mclow.lists, EricWF.
I'm breaking this up into independent bite-sized ~500 line reviews instead of a single 5200 line monster in hopes that we can turnaround changes quickly. This first piece implements the new bullet for `common_type`, and all of `common_reference` and `basic_common_reference`. I've conservatively hidden everything behind `_LIBCPP_STD_VER > 17`, although it could be made available in C++11 mode with some edits. https://reviews.llvm.org/D49118 Files: include/type_traits test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.pass.cpp test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp
Index: test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp =================================================================== --- test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp +++ test/std/utilities/meta/meta.trans/meta.trans.other/common_type.pass.cpp @@ -83,13 +83,16 @@ template <class T1, class T2> using TernaryOp = typename TernaryOpImp<T1, T2>::type; +// (4.1) // -- If sizeof...(T) is zero, there shall be no member type. void test_bullet_one() { static_assert(no_common_type<>::value, ""); } -// If sizeof...(T) is one, let T0 denote the sole type constituting the pack T. -// The member typedef-name type shall denote the same type as decay_t<T0>. +// (4.2) +// -- If sizeof...(T) is one, let T0 denote the sole type constituting the pack +// T. The member typedef-name type shall denote the same type, if any, as +// common_type_t<T0, T0>; otherwise there shall be no member type. void test_bullet_two() { static_assert(std::is_same<CommonType<void>, void>::value, ""); static_assert(std::is_same<CommonType<int>, int>::value, ""); @@ -110,11 +113,11 @@ static_assert(std::is_same<CommonType<T, U>, CommonType<DT, DU>>::value, ""); } -// (3.3) +// (4.3) // -- If sizeof...(T) is two, let the first and second types constituting T be // denoted by T1 and T2, respectively, and let D1 and D2 denote the same types // as decay_t<T1> and decay_t<T2>, respectively. -// (3.3.1) +// (4.3.1) // -- If is_same_v<T1, D1> is false or is_same_v<T2, D2> is false, let C // denote the same type, if any, as common_type_t<D1, D2>. void test_bullet_three_one() { @@ -148,16 +151,19 @@ } } -// (3.3) +// (4.3) // -- If sizeof...(T) is two, let the first and second types constituting T be // denoted by T1 and T2, respectively, and let D1 and D2 denote the same types // as decay_t<T1> and decay_t<T2>, respectively. -// (3.3.1) +// (4.3.1) // -- If [...] -// (3.3.2) -// -- Otherwise, let C denote the same type, if any, as +// (4.3.2) +// -- [Note: [...] +// (4.3.3) +// -- Otherwise, if // decay_t<decltype(false ? declval<D1>() : declval<D2>())> -void test_bullet_three_two() { +// denotes a type, let C denote that type. +void test_bullet_three_three() { { using T1 = int const*; using T2 = int*; @@ -188,7 +194,16 @@ } } -// (3.4) +#if TEST_STD_VER > 17 +// (4.3.4) +// -- Otherwise, if COND_RES(CREF(D1), CREF(D2)) denotes a type, let C denote +// the type decay_t<COND_RES(CREF(D1), CREF(D2))>. +void test_bullet_three_four() { + static_assert(std::is_same_v<CommonType<std::reference_wrapper<int>, int>, int>); +} +#endif + +// (4.4) // -- If sizeof...(T) is greater than two, let T1, T2, and R, respectively, // denote the first, second, and (pack of) remaining types constituting T. // Let C denote the same type, if any, as common_type_t<T1, T2>. If there is @@ -291,7 +306,10 @@ test_bullet_one(); test_bullet_two(); test_bullet_three_one(); - test_bullet_three_two(); + test_bullet_three_three(); +# if TEST_STD_VER > 17 + test_bullet_three_four(); +# endif test_bullet_four(); #endif Index: test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.pass.cpp =================================================================== --- /dev/null +++ test/std/utilities/meta/meta.trans/meta.trans.other/common_reference.pass.cpp @@ -0,0 +1,226 @@ +//===----------------------------------------------------------------------===// +// +// 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 <type_traits> + +using std::common_reference; +using std::common_reference_t; +using std::is_same_v; +using std::void_t; + +template <class, class = void> +constexpr bool is_trait = false; +template <class T> +constexpr bool is_trait<T, void_t<typename T::type>> = true; + +// 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>...>> + {}; +} + +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; + }; +} + +// (6.1) +// -- If sizeof...(T) is zero, there shall be no member type. +void test_bullet_one() { + static_assert(!is_trait<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. +void test_bullet_two() { + 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. +void test_bullet_three_one() { + { + 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&&, 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. +void test_bullet_three_two() { + 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. +void test_bullet_three_three() { + 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. +void test_bullet_three_four() { + { + 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. +void test_bullet_three_five() { + static_assert(!is_trait< + 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...>. +void test_bullet_four_one() { + 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. +void test_bullet_four_two() { + static_assert(!is_trait<common_reference<int, short, int, char*>>); +} + +int main() { + 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(); +} Index: include/type_traits =================================================================== --- include/type_traits +++ include/type_traits @@ -350,9 +350,9 @@ template <class T> inline constexpr bool is_nothrow_move_assignable_v = is_nothrow_move_assignable<T>::value; // C++17 template <class T, class U> inline constexpr bool is_nothrow_swappable_with_v - = is_nothrow_swappable_with<T, U>::value; // C++17 + = is_nothrow_swappable_with<T, U>::value; // C++17 template <class T> inline constexpr bool is_nothrow_swappable_v - = is_nothrow_swappable<T>::value; // C++17 + = is_nothrow_swappable<T>::value; // C++17 template <class T> inline constexpr bool is_nothrow_destructible_v = is_nothrow_destructible<T>::value; // C++17 template <class T> inline constexpr bool has_virtual_destructor_v @@ -395,6 +395,11 @@ template<class B> inline constexpr bool negation_v = negation<B>::value; // C++17 + template <class T, class U, template <class> class TQual, template <class> class UQual> + struct basic_common_reference { }; // C++20 + template <class... T> struct common_reference; // C++20 + template <class... T> // C++20 + using common_reference_t = typename common_reference<T...>::type; // C++20 } */ @@ -414,6 +419,11 @@ template <class> struct __void_t { typedef void type; }; +#if _LIBCPP_STD_VER > 14 +#define __cpp_lib_void_t 201411 +template <class...> using void_t = void; +#endif // _LIBCPP_STD_VER > 14 + template <class _Tp> struct __identity { typedef _Tp type; }; @@ -2098,8 +2108,33 @@ // bullet 3 - sizeof...(Tp) == 2 +#if _LIBCPP_STD_VER > 17 +// Let COND_RES(X, Y) be: +template <class _Tp, class _Up> +using __cond_res = + decltype(false ? _VSTD::declval<_Tp(&)()>()() : _VSTD::declval<_Up(&)()>()()); + +template <class _Tp, class _Up, class = void> +struct __common_type3 {}; + +// sub-bullet 4 - "if COND_RES(CREF(D1), CREF(D2)) denotes a type..." +template <class _Tp> +using __cref = add_lvalue_reference_t<const _Tp>; + +template <class _Tp, class _Up> +struct __common_type3<_Tp, _Up, void_t<__cond_res<__cref<_Tp>, __cref<_Up>>>> +{ + using type = decay_t<__cond_res<__cref<_Tp>, __cref<_Up>>>; +}; + +template <class _Tp, class _Up, class = void> +struct __common_type2_imp : __common_type3<_Tp, _Up> {}; +#else // ^^^ _LIBCPP_STD_VER > 17 / _LIBCPP_STD_VER <= 17 vvv template <class _Tp, class _Up, class = void> struct __common_type2_imp {}; +#endif // _LIBCPP_STD_VER > 17 + +// sub-bullet 3 - "if decay_t<decltype(false ? declval<D1>() : declval<D2>())> ..." template <class _Tp, class _Up> struct __common_type2_imp<_Tp, _Up, @@ -2112,6 +2147,8 @@ )>::type type; }; +// sub-bullet 1 - "If is_same_v<T1, D1> is false or ..." + template <class _Tp, class _Up, class _DTp = typename decay<_Tp>::type, class _DUp = typename decay<_Up>::type> @@ -2158,7 +2195,174 @@ template <class ..._Tp> using common_type_t = typename common_type<_Tp...>::type; #endif -#endif // _LIBCPP_HAS_NO_VARIADICS +#if _LIBCPP_STD_VER > 17 + +template <class, class, template <class> class, template <class> class> +struct _LIBCPP_TEMPLATE_VIS basic_common_reference +{}; + +// Let COPYCV(FROM, TO) be an alias for type TO with the addition of FROM’s +// top-level cv-qualifiers. +template <class _Tp> +struct __copy_cv_ +{ + template <class _Up> using __fn = _Up; +}; +template <class _Tp> +struct __copy_cv_<const _Tp> +{ + template <class _Up> using __fn = add_const_t<_Up>; +}; +template <class _Tp> +struct __copy_cv_<volatile _Tp> +{ + template <class _Up> using __fn = add_volatile_t<_Up>; +}; +template <class _Tp> +struct __copy_cv_<const volatile _Tp> +{ + template <class _Up> using __fn = add_cv_t<_Up>; +}; +template <class _From, class _To> +using __copy_cv = typename __copy_cv_<_From>::template __fn<_To>; + +// Let XREF(A) denote a unary class [sic] template T such [...] +// [Note: XREF(A) is __xref<A>::template __fn] +template <class _Tp> +struct __xref +{ + template <class _Up> using __fn = __copy_cv<_Tp, _Up>; +}; +template <class _Tp> +struct __xref<_Tp&> +{ + template <class _Up> using __fn = add_lvalue_reference_t<__copy_cv<_Tp, _Up>>; +}; +template <class _Tp> +struct __xref<_Tp&&> +{ + template <class _Up> using __fn = add_rvalue_reference_t<__copy_cv<_Tp, _Up>>; +}; + +template <class _Tp, class _Up, + class _Result = __cond_res<__copy_cv<_Tp, _Up>&, __copy_cv<_Up, _Tp>&>, + class = enable_if_t<is_lvalue_reference_v<_Result>>> +using __ll_common_ref = _Result; + +template <class _Tp, class _Up> +using __rr_common_ref = remove_reference_t<__ll_common_ref<_Tp, _Up>>&&; + +// Note C: For the common_reference trait applied to a parameter pack [...] + +// bullet 1 - sizeof...(T) == 0 + +template <class...> struct _LIBCPP_TEMPLATE_VIS common_reference {}; + +template <class... _Types> +using common_reference_t = typename common_reference<_Types...>::type; + +// bullet 2 - sizeof...(T) == 1 + +template <class _Tp> +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp> +{ + using type = _Tp; +}; + +// bullet 3 - sizeof...(T) == 2 + +// sub-bullet 4 & 5 - [...] if common_type_t<T1, T2> is well-formed [...] +// - Otherwise, there shall be no member type. + +template <class _Tp, class _Up, class = void> +struct __common_reference4 + : common_type<_Tp, _Up> +{}; + +// sub-bullet 3 - [...] if COND_RES(T1, T2) is well-formed [...] + +template <class _Tp, class _Up> +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 _Tp, class _Up> +using __basic_common_ref = typename basic_common_reference< + remove_cvref_t<_Tp>, remove_cvref_t<_Up>, + __xref<_Tp>::template __fn, __xref<_Up>::template __fn>::type; + +template <class _Tp, class _Up, class = void> +struct __common_reference3 + : __common_reference4<_Tp, _Up> +{}; + +template <class _Tp, class _Up> +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 <class _Tp, class _Up, class = void> +struct __common_reference2 + : __common_reference3<_Tp, _Up> +{}; + +template <class _Tp, class _Up> +struct __common_reference2<_Tp&, _Up&, void_t<__ll_common_ref<_Tp, _Up>>> +{ + using type = __ll_common_ref<_Tp, _Up>; +}; + +template <class _Tp, class _Up> +struct __common_reference2<_Tp&&, _Up&, enable_if_t< + is_convertible_v<_Tp&&, __ll_common_ref<const _Tp, _Up>>>> +{ + using type = __ll_common_ref<const _Tp, _Up>; +}; + +template <class _Tp, class _Up> +struct __common_reference2<_Tp&, _Up&&, enable_if_t< + is_convertible_v<_Up&&, __ll_common_ref<const _Up, _Tp>>>> +{ + using type = __ll_common_ref<const _Up, _Tp>; +}; + +template <class _Tp, class _Up> +struct __common_reference2<_Tp&&, _Up&&, enable_if_t< + is_convertible_v<_Tp&&, __rr_common_ref<_Tp, _Up>> && + is_convertible_v<_Up&&, __rr_common_ref<_Tp, _Up>>>> +{ + using type = __rr_common_ref<_Tp, _Up>; +}; + +template <class _Tp, class _Up> +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up> + : __common_reference2<_Tp, _Up> +{}; + +// bullet 4 - sizeof...(T) > 2 + +template <class _Void, class _Tp, class _Up, class... _Types> +struct __fold_common_reference +{}; +template <class _Tp, class _Up, class... _Types> +struct __fold_common_reference<void_t<common_reference_t<_Tp, _Up>>, _Tp, _Up, _Types...> + : common_reference<common_reference_t<_Tp, _Up>, _Types...> +{}; + +template <class _Tp, class _Up, class _Vp, class... _Rest> +struct _LIBCPP_TEMPLATE_VIS common_reference<_Tp, _Up, _Vp, _Rest...> + : __fold_common_reference<void, _Tp, _Up, _Vp, _Rest...> +{}; + +#endif // _LIBCPP_STD_VER > 17 + +#endif // _LIBCPP_HAS_NO_VARIADICS // is_assignable @@ -4746,12 +4950,7 @@ #endif // _LIBCPP_CXX03_LANG -#if _LIBCPP_STD_VER > 14 - -#define __cpp_lib_void_t 201411 -template <class...> using void_t = void; - -# ifndef _LIBCPP_HAS_NO_VARIADICS +#if _LIBCPP_STD_VER > 14 && !defined(_LIBCPP_HAS_NO_VARIADICS) template <class... _Args> struct conjunction : __and_<_Args...> {}; template<class... _Args> @@ -4769,8 +4968,7 @@ template<class _Tp> _LIBCPP_INLINE_VAR constexpr bool negation_v = negation<_Tp>::value; -# endif // _LIBCPP_HAS_NO_VARIADICS -#endif // _LIBCPP_STD_VER > 14 +#endif // _LIBCPP_STD_VER > 14 && !defined(_LIBCPP_HAS_NO_VARIADICS) // These traits are used in __tree and __hash_table #ifndef _LIBCPP_CXX03_LANG
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits