Skip to content

[libc++] Disallow specializing common_reference #141465

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libcxx/docs/ReleaseNotes/21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ Potentially breaking changes
- The implementation of ``num_put::do_put`` has been replaced to improve the performance, which can lead to different
output when printing pointers.

- User-defined specializations of ``std::common_reference`` are diagnosed now. To customize the common reference type, ``std::basic_common_reference`` should be specialized instead.

Announcements About Future Releases
-----------------------------------

Expand Down
15 changes: 10 additions & 5 deletions libcxx/include/__type_traits/common_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,18 @@ struct __common_ref {};
// Note C: For the common_reference trait applied to a parameter pack [...]

template <class...>
struct common_reference;
struct _LIBCPP_NO_SPECIALIZATIONS common_reference;

template <class... _Types>
using common_reference_t = typename common_reference<_Types...>::type;

template <class, class, template <class> class, template <class> class>
struct basic_common_reference {};
Comment on lines +117 to +118
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm moving basic_common_reference here, because it's a standard library component and it's a bit weird to me to leave it in an "implementation detail code section".


_LIBCPP_DIAGNOSTIC_PUSH
# if __has_warning("-Winvalid-specialization")
_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Winvalid-specialization")
# endif
// bullet 1 - sizeof...(T) == 0
template <>
struct common_reference<> {};
Expand Down Expand Up @@ -145,9 +152,6 @@ struct __common_reference_sub_bullet1<_Tp, _Up> {

// sub-bullet 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.
template <class, class, template <class> class, template <class> class>
struct basic_common_reference {};

template <class _Tp, class _Up>
using __basic_common_reference_t _LIBCPP_NODEBUG =
typename basic_common_reference<remove_cvref_t<_Tp>,
Expand Down Expand Up @@ -180,10 +184,11 @@ struct __common_reference_sub_bullet3 : common_type<_Tp, _Up> {};
template <class _Tp, class _Up, class _Vp, class... _Rest>
requires requires { typename common_reference_t<_Tp, _Up>; }
struct common_reference<_Tp, _Up, _Vp, _Rest...> : common_reference<common_reference_t<_Tp, _Up>, _Vp, _Rest...> {};
_LIBCPP_DIAGNOSTIC_POP

// bullet 5 - Otherwise, there shall be no member `type`.
template <class...>
struct common_reference {};
struct _LIBCPP_NO_SPECIALIZATIONS common_reference {};

#endif // _LIBCPP_STD_VER >= 20

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,5 +186,12 @@ struct std::enable_if<true, S>; // expected-error {{cannot be specialized}}
# if TEST_STD_VER >= 20
template <>
struct std::integral_constant<S, {}>; // expected-error {{cannot be specialized}}

template <>
struct std::common_reference<S>; // expected-error {{cannot be specialized}}
template <>
struct std::common_reference<S, S>; // expected-error {{cannot be specialized}}
template <>
struct std::common_reference<S, S, S>; // expected-error {{cannot be specialized}}
# endif
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -78,16 +78,10 @@ struct missing_iter_value_t {
};
static_assert(!check_indirectly_readable<missing_iter_value_t>());

struct unrelated_lvalue_ref_and_rvalue_ref {};

struct iter_ref1 {};
template <>
struct std::common_reference<iter_ref1&, iter_ref1&&> {};

template <>
struct std::common_reference<iter_ref1&&, iter_ref1&> {};

static_assert(!std::common_reference_with<iter_ref1&, iter_ref1&&>);
struct iter_ref1 {
iter_ref1(const iter_ref1&) = delete;
iter_ref1(iter_ref1&&) = delete;
};

struct bad_iter_reference_t {
using value_type = int;
Expand Down Expand Up @@ -128,24 +122,9 @@ struct different_reference_types_with_common_reference {
static_assert(check_indirectly_readable<different_reference_types_with_common_reference>());

struct iter_ref4 {
operator iter_rvalue_ref() const;
operator iter_rvalue_ref();
};

template <template <class> class XQual, template <class> class YQual>
struct std::basic_common_reference<iter_ref4, iter_rvalue_ref, XQual, YQual> {
using type = iter_rvalue_ref;
};
template <template <class> class XQual, template <class> class YQual>
struct std::basic_common_reference<iter_rvalue_ref, iter_ref4, XQual, YQual> {
using type = iter_rvalue_ref;
};

// FIXME: This is UB according to [meta.rqmts], and there is no exception for common_reference.
template <>
struct std::common_reference<iter_ref4 const&, iter_rvalue_ref&&> {};
template <>
struct std::common_reference<iter_rvalue_ref&&, iter_ref4 const&> {};

static_assert(std::common_reference_with<iter_ref4&&, iter_rvalue_ref&&>);
static_assert(!std::common_reference_with<iter_ref4 const&, iter_rvalue_ref&&>);

Expand Down
Loading