On Mon, Oct 13, 2025 at 6:29 AM Patrick Palka <[email protected]> wrote:
> On Wed, 8 Oct 2025, Tomasz Kaminski wrote: > > > > > > > On Tue, Oct 7, 2025 at 5:10 AM Patrick Palka <[email protected]> wrote: > > Tested on x86_64-pc-linux-gnu, does this look OK for trunk? I > wonder > > about backports, for either the whole paper or just the > common_reference > > change in <type_traits> for avoiding dangling references. > > > > -- >8 -- > > > > This patch implements this paper as a DR against C++20 (as do MSVC > and > > libc++). > > > > PR libstdc++/120446 > > > > libstdc++-v3/ChangeLog: > > > > * include/bits/refwrap.h (__detail::__is_ref_wrapper): > > Define as per P2655R3 for C++20. > > (__detail::__ref_wrap_common_reference_exists_with): > > Likewise. > > (basic_common_reference): Define partial specializations > using > > the above as per P2655R3 for C++20. > > * include/bits/version.def (common_reference): New. > > (common_reference_wrapper): New. > > * include/bits/version.h: Regenerate. > > * include/std/functional > (__glibcxx_want_common_reference_wrapper): > > Define. > > * include/std/type_traits > (__glibcxx_want_common_reference): > > Define. > > (__common_reference_impl<T1, T2, 1>): Add pointer > convertibility > > constraints as per P2655R3. > > * > libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc: New > > test. > > * > libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc: New > > test. > > --- > > > > The comment are mostly for additional tests, and for further separating > changes > > to basic_common_reference generic specialization and other. > > > > libstdc++-v3/include/bits/refwrap.h | 34 > +++++++++++++++++++ > > libstdc++-v3/include/bits/version.def | 16 +++++++++ > > libstdc++-v3/include/bits/version.h | 20 +++++++++++ > > libstdc++-v3/include/std/functional | 1 + > > libstdc++-v3/include/std/type_traits | 7 ++++ > > .../20_util/common_reference/p2655r3.cc | 15 ++++++++ > > .../20_util/reference_wrapper/p2655r3.cc | 33 > ++++++++++++++++++ > > 7 files changed, 126 insertions(+) > > create mode 100644 > libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc > > create mode 100644 > libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc > > > > diff --git a/libstdc++-v3/include/bits/refwrap.h > b/libstdc++-v3/include/bits/refwrap.h > > index 612715e9b7bd..5d9f8c8f49f9 100644 > > --- a/libstdc++-v3/include/bits/refwrap.h > > +++ b/libstdc++-v3/include/bits/refwrap.h > > @@ -457,6 +457,40 @@ _GLIBCXX_MEM_FN_TRAITS(&& noexcept, > false_type, true_type) > > > > /// @} > > > > +#if __glibcxx_common_reference_wrapper // C++ >= 20 > > + namespace __detail > > + { > > + template<typename _Tp> > > + constexpr bool __is_ref_wrapper = false; > > + > > + template<typename _Tp> > > + constexpr bool __is_ref_wrapper<reference_wrapper<_Tp>> = > true; > > + > > + template<typename _Rp, typename _Tp, typename _RQual, > typename _TQual> > > + concept __ref_wrap_common_reference_exists_with = > __is_ref_wrapper<_Rp> > > + && requires { typename common_reference_t<typename > _Rp::type&, _TQual>; } > > > > This requires does not seem to be necessary, as > common_reference_t<typename _Rp::type&, _TQual> > > is used as convertible_to argument, and if expressions inside > constraints list is ill-formed > > the it will evaluate to false. I know that is exactly what standard > specifies, but still. > > But in 'requires { Y; } && convertible_to<X, Y>' we first instantiate > Y's mapped template argument, whereas in just 'convertible_to<X, Y>' we > first instantiate X's mapped template argument. Such a change in the > order of instantiation could be observable; I'm not positive it's > unobservable in this particular scenario. So I'd prefer following the > standard. If it's truly unnecessary maybe we should remove it > editiorially from the standard first. > > > + && convertible_to<_RQual, common_reference_t<typename > _Rp::type&, _TQual>>; > > + } // namespace __detail > > + > > + template<typename _Rp, typename _Tp, > > + template<typename> class _RQual, template<typename> > class _TQual> > > + requires > __detail::__ref_wrap_common_reference_exists_with<_Rp, _Tp, > > + > _RQual<_Rp>, _TQual<_Tp>> > > + && (!__detail::__ref_wrap_common_reference_exists_with<_Tp, > _Rp, > > + > _TQual<_Tp>, _RQual<_Rp>>) > > + struct basic_common_reference<_Rp, _Tp, _RQual, _TQual> > > + { using type = common_reference_t<typename _Rp::type&, > _TQual<_Tp>>; }; > > + > > + template<typename _Tp, typename _Rp, > > + template<typename> class _TQual, template<typename> > class _RQual> > > + requires > __detail::__ref_wrap_common_reference_exists_with<_Rp, _Tp, > > + > _RQual<_Rp>, _TQual<_Tp>> > > + && (!__detail::__ref_wrap_common_reference_exists_with<_Tp, > _Rp, > > + > _TQual<_Tp>, _RQual<_Rp>>) > > + struct basic_common_reference<_Tp, _Rp, _TQual, _RQual> > > + { using type = common_reference_t<typename _Rp::type&, > _TQual<_Tp>>; }; > > +#endif > > + > > _GLIBCXX_END_NAMESPACE_VERSION > > } // namespace std > > > > diff --git a/libstdc++-v3/include/bits/version.def > b/libstdc++-v3/include/bits/version.def > > index 3a26234f87ed..49d8d17e6677 100644 > > --- a/libstdc++-v3/include/bits/version.def > > +++ b/libstdc++-v3/include/bits/version.def > > @@ -1774,6 +1774,22 @@ ftms = { > > }; > > }; > > > > +ftms = { > > + name = common_reference; > > + values = { > > + v = 202302; > > + cxxmin = 20; // We treat P2655R3 as a DR against C++20. > > + }; > > +}; > > + > > +ftms = { > > + name = common_reference_wrapper; > > + values = { > > + v = 202302; > > + cxxmin = 20; // We treat P2655R3 as a DR against C++20. > > + }; > > +}; > > + > > ftms = { > > name = formatters; > > values = { > > diff --git a/libstdc++-v3/include/bits/version.h > b/libstdc++-v3/include/bits/version.h > > index 46e4c1121e7a..3176a80f32e3 100644 > > --- a/libstdc++-v3/include/bits/version.h > > +++ b/libstdc++-v3/include/bits/version.h > > @@ -1983,6 +1983,26 @@ > > #endif /* !defined(__cpp_lib_flat_set) && > defined(__glibcxx_want_flat_set) */ > > #undef __glibcxx_want_flat_set > > > > +#if !defined(__cpp_lib_common_reference) > > +# if (__cplusplus >= 202002L) > > +# define __glibcxx_common_reference 202302L > > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_common_reference) > > +# define __cpp_lib_common_reference 202302L > > +# endif > > +# endif > > +#endif /* !defined(__cpp_lib_common_reference) && > defined(__glibcxx_want_common_reference) */ > > +#undef __glibcxx_want_common_reference > > + > > +#if !defined(__cpp_lib_common_reference_wrapper) > > +# if (__cplusplus >= 202002L) > > +# define __glibcxx_common_reference_wrapper 202302L > > +# if defined(__glibcxx_want_all) || > defined(__glibcxx_want_common_reference_wrapper) > > +# define __cpp_lib_common_reference_wrapper 202302L > > +# endif > > +# endif > > +#endif /* !defined(__cpp_lib_common_reference_wrapper) && > defined(__glibcxx_want_common_reference_wrapper) */ > > +#undef __glibcxx_want_common_reference_wrapper > > + > > #if !defined(__cpp_lib_formatters) > > # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED > > # define __glibcxx_formatters 202302L > > diff --git a/libstdc++-v3/include/std/functional > b/libstdc++-v3/include/std/functional > > index 8ad73b343bda..6f7f2f627a2a 100644 > > --- a/libstdc++-v3/include/std/functional > > +++ b/libstdc++-v3/include/std/functional > > @@ -64,6 +64,7 @@ > > #define __glibcxx_want_not_fn > > #define __glibcxx_want_ranges > > #define __glibcxx_want_reference_wrapper > > +#define __glibcxx_want_common_reference_wrapper > > #define __glibcxx_want_transparent_operators > > #include <bits/version.h> > > > > diff --git a/libstdc++-v3/include/std/type_traits > b/libstdc++-v3/include/std/type_traits > > index 8b5110464e50..47cbf21b4c1d 100644 > > --- a/libstdc++-v3/include/std/type_traits > > +++ b/libstdc++-v3/include/std/type_traits > > @@ -41,6 +41,7 @@ > > > > #define __glibcxx_want_bool_constant > > #define __glibcxx_want_bounded_array_traits > > +#define __glibcxx_want_common_reference > > #define __glibcxx_want_constant_wrapper > > #define __glibcxx_want_has_unique_object_representations > > #define __glibcxx_want_integral_constant_callable > > @@ -4223,6 +4224,12 @@ template<typename _Ret, typename _Fn, > typename... _Args> > > template<typename _Tp1, typename _Tp2> > > requires is_reference_v<_Tp1> && is_reference_v<_Tp2> > > && requires { typename __common_ref<_Tp1, _Tp2>; } > > +#if __cpp_lib_common_reference // C++ >= 20 > > + && is_convertible_v<add_pointer_t<_Tp1>, > > + add_pointer_t<__common_ref<_Tp1, _Tp2>>> > > + && is_convertible_v<add_pointer_t<_Tp2>, > > + add_pointer_t<__common_ref<_Tp1, _Tp2>>> > > +#endif > > > > I would prefer this change to be in a separate commit from > common_reference one, in case if we get regression from it. > > Sounds good > > > > > struct __common_reference_impl<_Tp1, _Tp2, 1> > > { using type = __common_ref<_Tp1, _Tp2>; }; > > > > diff --git > a/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc > b/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc > > new file mode 100644 > > index 000000000000..bc0372f79686 > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/20_util/common_reference/p2655r3.cc > > @@ -0,0 +1,15 @@ > > +// P2655R3 - common_reference_t of reference_wrapper Should Be a > Reference Type > > +// Implemented as a DR against C++20 > > +// { dg-do compile { target c++20 } } > > + > > +#include <type_traits> > > + > > +#if __cpp_lib_common_reference != 202302L > > +# error "Feature-test macro __cpp_lib_common_reference has wrong > value in <type_traits>" > > +#endif > > + > > +struct A { }; > > +struct B { operator A&() const; }; > > + > > +static_assert(std::is_same_v<std::common_reference_t<A&, const > B&>, A&>); > > +static_assert(std::is_same_v<std::common_reference_t<const B&, > A&>, A&>); > > diff --git > a/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc > b/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc > > new file mode 100644 > > index 000000000000..f66c2821554a > > --- /dev/null > > +++ b/libstdc++-v3/testsuite/20_util/reference_wrapper/p2655r3.cc > > @@ -0,0 +1,33 @@ > > +// P2655R3 - common_reference_t of reference_wrapper Should Be a > Reference Type > > +// Implemented as a DR against C++20 > > +// { dg-do compile { target c++20 } } > > > > Could you add following tests: > > * common_reference of reference_wrapper<T> and reference_wrapper<U>, > > including reference_wrapper<reference_wrapper<T>> and > reference_wrapper<U> > > You could do that by having a wrapper template parameter, and passing > > template<typename T> using Id = T; for normal cases > > (I suspect that something may be inconsistent here). > > > > * common_reference of pair/tuple reference wrapper<T> and U&, > > maybe in separate file. > > I added some extra testcases, but I'm afraid I don't know precisely what > you want added. I couldn't come up with a valid pair/tuple + > reference_wrapper testcase for which common_reference_t is well-formed, > or a ref<ref<T>> testcase for which behavior is changed by this patch. > If you have provide concrete testcases I can add them. > Becaues of the basic_common_reference for tuple + tuple-like specialization (https://eel.is/c++draft/tuple.common.ref). I would expect that common_reference_t<tuple<reference_wraper<T>> cv1 ref1, tuple<U> cv2 ref2>> produces tuple<common_reference_<T&, U cv2 ref2>> But, I think we could add them as separate commits later. Examples would be: tuple<refernce_wrapper<int>> + tuple<int> -> tuple<int> tuple<refernce_wrapper<int>> + tuple<int>& -> tuple<int&> tuple<refernce_wrapper<int>> + tuple<int&> -> tuple<int&> tuple<reference_wrapper<int>> + const tuple<int> -> tuple<int> tuple<refernce_wrapper<int>> + const tuple<int>& -> tuple<const int&> // reference from reference wrapper is ignored tuple<const refernce_wrapper<int>&> + tuple<int>& -> tuple<int&> const tuple<refernce_wrapper<int>>& + tuple<int>& -> tuple<int&> tuple<reference_wrapper<int>> + tuple<reference_wrapper<const int>> -> tuple<reference_wrapper<const int>>? > > + > > +#include <functional> > > + > > +#if __cpp_lib_common_reference_wrapper != 202302L > > +# error "Feature-test macro __cpp_lib_common_reference_wrapper > has wrong value in <functional>" > > +#endif > > + > > +using std::is_same_v; > > +using std::common_reference_t; > > +using std::reference_wrapper; > > + > > +static_assert(is_same_v<common_reference_t<const > reference_wrapper<int>&, int&>, int&>); > > +static_assert(is_same_v<common_reference_t<int&, const > reference_wrapper<int>&>, int&>); > > + > > > +static_assert(is_same_v<common_reference_t<reference_wrapper<int>, int&>, > > + common_reference_t<int&, int&>>); > > > +static_assert(is_same_v<common_reference_t<reference_wrapper<int>, const > int&>, > > + common_reference_t<int&, const int&>>); > > > +static_assert(is_same_v<common_reference_t<reference_wrapper<const int>, > int&>, > > + common_reference_t<const int&, int&>>); > > + > > +static_assert(is_same_v<common_reference_t<int&, > reference_wrapper<int>>, > > + common_reference_t<int&, int&>>); > > +static_assert(is_same_v<common_reference_t<const int&, > reference_wrapper<int>>, > > + common_reference_t<int&, const int&>>); > > +static_assert(is_same_v<common_reference_t<int&, > reference_wrapper<const int>>, > > + common_reference_t<const int&, int&>>); > > + > > > +static_assert(is_same_v<common_reference_t<reference_wrapper<int>&, > reference_wrapper<int>&>, > > + reference_wrapper<int>&>); > > -- > > 2.51.0.433.g45547b60ac > > > > > >
