From a6b45b741e25936de1e7a2c2a2511c9eeb4f156f Mon Sep 17 00:00:00 2001 From: Hui Xie Date: Sun, 25 May 2025 14:09:34 +0100 Subject: [PATCH 1/9] [libc++] P2655R3 common_reference_t of reference_wrapper Should Be a Reference Type --- .../include/__functional/reference_wrapper.h | 29 ++++ .../include/__type_traits/common_reference.h | 11 +- .../refwrap/common_reference.compile.pass.cpp | 153 ++++++++++++++++++ .../common_reference.compile.pass.cpp | 58 ++++--- 4 files changed, 226 insertions(+), 25 deletions(-) create mode 100644 libcxx/test/std/utilities/function.objects/refwrap/common_reference.compile.pass.cpp diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h index 11ba364369428..ebeef321f1dc4 100644 --- a/libcxx/include/__functional/reference_wrapper.h +++ b/libcxx/include/__functional/reference_wrapper.h @@ -11,15 +11,18 @@ #define _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H #include <__compare/synth_three_way.h> +#include <__concepts/convertible_to.h> #include <__config> #include <__functional/weak_result_type.h> #include <__memory/addressof.h> +#include <__type_traits/common_reference.h> #include <__type_traits/desugars_to.h> #include <__type_traits/enable_if.h> #include <__type_traits/invoke.h> #include <__type_traits/is_const.h> #include <__type_traits/is_core_convertible.h> #include <__type_traits/is_same.h> +#include <__type_traits/is_specialization.h> #include <__type_traits/remove_cvref.h> #include <__type_traits/void_t.h> #include <__utility/declval.h> @@ -156,6 +159,32 @@ template inline const bool __desugars_to_v<_CanonicalTag, reference_wrapper<_Operation>, _Args...> = __desugars_to_v<_CanonicalTag, _Operation, _Args...>; +#if _LIBCPP_STD_VER >= 20 + +template +inline constexpr bool __is_ref_wrapper = __is_specialization_v<_Tp, reference_wrapper>; + +template +concept __ref_wrap_common_reference_exists_with = __is_ref_wrapper<_Rp> && requires { + typename common_reference_t; +} && convertible_to<_RpQual, common_reference_t>; + +template class _RpQual, template class _TpQual> + requires(__ref_wrap_common_reference_exists_with<_Rp, _Tp, _RpQual<_Rp>, _TpQual<_Tp>> && + !__ref_wrap_common_reference_exists_with<_Tp, _Rp, _TpQual<_Tp>, _RpQual<_Rp>>) +struct basic_common_reference<_Rp, _Tp, _RpQual, _TpQual> { + using type = common_reference_t>; +}; + +template class _TpQual, template class _RpQual> + requires(__ref_wrap_common_reference_exists_with<_Rp, _Tp, _RpQual<_Rp>, _TpQual<_Tp>> && + !__ref_wrap_common_reference_exists_with<_Tp, _Rp, _TpQual<_Tp>, _RpQual<_Rp>>) +struct basic_common_reference<_Tp, _Rp, _TpQual, _RpQual> { + using type = common_reference_t>; +}; + +#endif // _LIBCPP_STD_VER >= 20 + _LIBCPP_END_NAMESPACE_STD #endif // _LIBCPP___FUNCTIONAL_REFERENCE_WRAPPER_H diff --git a/libcxx/include/__type_traits/common_reference.h b/libcxx/include/__type_traits/common_reference.h index 7df55f7fecfd5..59badb64267de 100644 --- a/libcxx/include/__type_traits/common_reference.h +++ b/libcxx/include/__type_traits/common_reference.h @@ -10,6 +10,7 @@ #define _LIBCPP___TYPE_TRAITS_COMMON_REFERENCE_H #include <__config> +#include <__type_traits/add_pointer.h> #include <__type_traits/common_type.h> #include <__type_traits/copy_cv.h> #include <__type_traits/copy_cvref.h> @@ -139,13 +140,17 @@ 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. +// sub-bullet 1 - Let R be COMMON-REF(T1, T2). If T1 and T2 are reference types, R is well-formed, and +// is_convertible_v, add_pointer_t> && is_convertible_v, add_pointer_t> is +// true, then the member typedef type denotes R. + 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>; } + requires is_reference_v<_Tp> && is_reference_v<_Up> && requires { typename __common_ref_t<_Tp, _Up>; } && + is_convertible_v, add_pointer_t<__common_ref_t<_Tp, _Up>>> && + is_convertible_v, add_pointer_t<__common_ref_t<_Tp, _Up>>> struct __common_reference_sub_bullet1<_Tp, _Up> { using type _LIBCPP_NODEBUG = __common_ref_t<_Tp, _Up>; }; diff --git a/libcxx/test/std/utilities/function.objects/refwrap/common_reference.compile.pass.cpp b/libcxx/test/std/utilities/function.objects/refwrap/common_reference.compile.pass.cpp new file mode 100644 index 0000000000000..142841a60eb90 --- /dev/null +++ b/libcxx/test/std/utilities/function.objects/refwrap/common_reference.compile.pass.cpp @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// +// +// UNSUPPORTED: c++03, c++11, c++14, c++17 +// +// common_reference specializations for reference_wrapper + +#include +#include +#include + +template +concept HasType = requires { typename T::type; }; + +template +concept check_XY = std::same_as>; + +template +concept check_YX = std::same_as>; + +template +concept check = check_XY && check_YX; + +template +concept check_none_XY = !HasType>; +template +concept check_none_YX = !HasType>; + +template +concept check_none = check_none_XY && check_none_YX; + +// https://eel.is/c++draft/meta.trans#other-2.4 +template +using CondRes = decltype(false ? std::declval()() : std::declval()()); + +template +struct Ternary {}; + +template + requires requires() { typename CondRes; } +struct Ternary { + using type = CondRes; +}; +template +using Ternary_t = typename Ternary::type; + +template +using Ref = std::reference_wrapper; + +using std::common_reference_t; +using std::same_as; + +// clang-format off +static_assert(check, int & >); +static_assert(check, int const& >); +static_assert(check, int & >); +static_assert(check, int const& >); +static_assert(check const&, int& >); +static_assert(check, const volatile int&>); + +// derived-base and implicit convertibles +struct B {}; +struct D : B {}; +struct C { + operator B&() const; +}; + +static_assert(check, D & >); +static_assert(check, D const&>); +static_assert(check, D const&>); + +static_assert(check, B & >); +static_assert(check, B const&>); +static_assert(check, B const&>); + +static_assert(std::same_as, B&>>); +static_assert(std::same_as, B const &>>); +static_assert(std::same_as, B const&>>); + +static_assert( check , C& >); +static_assert( check , C >); +static_assert( check, C >); +static_assert(!check , B& >); // Ref cannot be converted to B& +static_assert( check , C const&>); // was const B& before P2655R3 + + +using Ri = Ref; +using RRi = Ref>; +using RRRi = Ref>>; +static_assert(check); +static_assert(check); +static_assert(check); +static_assert(check); + +static_assert(check_none); +static_assert(check_none); +static_assert(check_none); +static_assert(check_none); + +static_assert(check_none); +static_assert(check_none); + + +template +struct Test { + // Check that reference_wrapper behaves the same as T& in common_reference. + + using R1 = common_reference_t; + using R2 = common_reference_t; + using R3 = common_reference_t; + using R4 = common_reference_t; + using R5 = common_reference_t; + + static_assert(same_as, T&>>); + static_assert(same_as, T const&>>); + static_assert(same_as, T&&>>); + static_assert(same_as, T const&&>>); + static_assert(same_as, T>>); + + // commute: + static_assert(same_as>>); + static_assert(same_as>>); + static_assert(same_as>>); + static_assert(same_as>>); + static_assert(same_as>>); + + // reference qualification of reference_wrapper is irrelevant + static_assert(same_as&, T&>>); + static_assert(same_as , T&>>); + static_assert(same_as const&, T&>>); + static_assert(same_as&&, T&>>); + static_assert(same_as const&&, T&>>); +}; + +// clang-format on +// Instantiate above checks: +template struct Test; +template struct Test>; + + +// reference_wrapper as both args is unaffected. +// subject to simple first rule of +static_assert(check&, Ref&, Ref&>); + +// double wrap is unaffected. +static_assert(check&, Ref>, Ref&>); \ No newline at end of file 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 index e802776f52bfc..8e3cad797d5a7 100644 --- 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 @@ -18,9 +18,7 @@ #include "test_macros.h" template -constexpr bool has_type = requires { - typename T::type; -}; +constexpr bool has_type = requires { typename T::type; }; // A slightly simplified variation of std::tuple template @@ -74,8 +72,10 @@ static_assert(std::is_same_v, void (&&)()>) // -- 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. +// -- Let R be COMMON-REF(T1, T2). If T1 and T2 are reference types, R is well-formed, +// and is_convertible_v, add_pointer_t> && is_convertible_v, add_pointer_t> +// is true, then the member typedef type denotes R. + struct B {}; struct D : B {}; static_assert(std::is_same_v, B&>); @@ -99,7 +99,19 @@ static_assert(std::is_same_v, static_assert(std::is_same_v, int const volatile&&>); static_assert(std::is_same_v, int const (&)[10]>); -static_assert(std::is_same_v, int const volatile (&)[10]>); +static_assert( + std::is_same_v, int const volatile (&)[10]>); + +// when conversion from pointers are not true +struct E {}; +struct F { + operator E&() const; +}; + +static_assert(!std::is_convertible_v); + +// The following should not use 6.3.1, but fallback to 6.3.3 +static_assert(std::is_same_v, E&>); // (6.3.2) // -- Otherwise, if basic_common_reference, @@ -136,8 +148,8 @@ static_assert(std::is_same_v, MyIntRef>) // -- Otherwise, if common_type_t is well-formed, then the member // typedef type denotes that type. struct moveonly { - moveonly() = default; - moveonly(moveonly&&) = default; + moveonly() = default; + moveonly(moveonly&&) = default; moveonly& operator=(moveonly&&) = default; }; struct moveonly2 : moveonly {}; @@ -169,14 +181,17 @@ static_assert(!has_type >); #if TEST_STD_VER > 20 static_assert(std::is_same_v>, std::tuple>); -static_assert(std::is_same_v, std::tuple>, std::tuple>); +static_assert( + std::is_same_v, std::tuple>, std::tuple>); static_assert(std::is_same_v, std::tuple>, std::tuple>); static_assert(std::is_same_v, std::tuple>, std::tuple>); -static_assert(std::is_same_v, std::tuple>, - std::tuple>); -static_assert(!has_type, std::tuple>>); +static_assert( + std::is_same_v, std::tuple>, + std::tuple>); +static_assert( + !has_type, std::tuple>>); static_assert(std::is_same_v, std::tuple>, std::tuple>); static_assert(std::is_same_v, std::tuple>, std::tuple>); @@ -185,26 +200,25 @@ static_assert(!has_type, std::tuple, int, X2>>); struct A {}; -template class TQual, template class UQual> +template