Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
3 changes: 3 additions & 0 deletions libcxx/docs/ReleaseNotes/21.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ 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 disallowed with the ``[[clang::no_specializations]]``
attribute. 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