diff --git a/libcxx/include/__exception/exception_ptr.h b/libcxx/include/__exception/exception_ptr.h --- a/libcxx/include/__exception/exception_ptr.h +++ b/libcxx/include/__exception/exception_ptr.h @@ -36,6 +36,7 @@ _LIBCPP_HIDE_FROM_ABI explicit operator bool() const _NOEXCEPT { return __ptr_ != nullptr; } +# if _LIBCPP_STD_VER < 20 friend _LIBCPP_HIDE_FROM_ABI bool operator==(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT { return __x.__ptr_ == __y.__ptr_; } @@ -43,6 +44,10 @@ friend _LIBCPP_HIDE_FROM_ABI bool operator!=(const exception_ptr& __x, const exception_ptr& __y) _NOEXCEPT { return !(__x == __y); } +# else + // This makes exception_ptr __trivially_equality_comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const exception_ptr&) const = default; +# endif friend _LIBCPP_FUNC_VIS exception_ptr current_exception() _NOEXCEPT; friend _LIBCPP_FUNC_VIS void rethrow_exception(exception_ptr); diff --git a/libcxx/include/__expected/unexpected.h b/libcxx/include/__expected/unexpected.h --- a/libcxx/include/__expected/unexpected.h +++ b/libcxx/include/__expected/unexpected.h @@ -108,6 +108,12 @@ return __x.__unex_ == __y.__unex_; } + // conforming extension: This is not observable other than through SFINAE on `u.operator==` + // This makes unexpected trivially equality comparable if _Err is trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const unexpected&) const + requires is_object_v<_Err> + = default; + private: _Err __unex_; }; diff --git a/libcxx/include/__iterator/wrap_iter.h b/libcxx/include/__iterator/wrap_iter.h --- a/libcxx/include/__iterator/wrap_iter.h +++ b/libcxx/include/__iterator/wrap_iter.h @@ -150,6 +150,12 @@ template friend class basic_string; template friend class _LIBCPP_TEMPLATE_VIS vector; template friend class _LIBCPP_TEMPLATE_VIS span; + +#if _LIBCPP_STD_VER >= 20 +public: + // This makes __wrap_iter trivially equality comparable if _Iter is trivially equality comparable + bool operator==(const __wrap_iter&) const = default; +#endif }; template diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -40,6 +40,7 @@ #include <__type_traits/is_nothrow_copy_constructible.h> #include <__type_traits/is_nothrow_default_constructible.h> #include <__type_traits/is_nothrow_move_assignable.h> +#include <__type_traits/is_object.h> #include <__type_traits/is_same.h> #include <__type_traits/is_swappable.h> #include <__type_traits/nat.h> @@ -592,6 +593,13 @@ tuple<_Args1...>& __first_args, tuple<_Args2...>& __second_args, __tuple_indices<_I1...>, __tuple_indices<_I2...>); #endif + +#if _LIBCPP_STD_VER >= 20 +public: + // conforming extension: This is not observable other than through SFINAE on `u.operator==` + // This makes pair trivially equality comparable if _T1 and _T2 are trivially equality comparable + bool operator==(const pair&) const requires (is_object_v<_T1> && is_object_v<_T2>) = default; +#endif }; #if _LIBCPP_STD_VER >= 17 diff --git a/libcxx/include/array b/libcxx/include/array --- a/libcxx/include/array +++ b/libcxx/include/array @@ -260,6 +260,12 @@ value_type* data() _NOEXCEPT {return __elems_;} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX17 const value_type* data() const _NOEXCEPT {return __elems_;} + +#if _LIBCPP_STD_VER >= 20 + // conforming extension: This is not observable other than through SFINAE on `u.operator==` + // This makes pair trivially equality comparable if _Tp is trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const array&) const = default; +#endif }; template diff --git a/libcxx/include/complex b/libcxx/include/complex --- a/libcxx/include/complex +++ b/libcxx/include/complex @@ -310,6 +310,12 @@ *this = *this / complex(__c.real(), __c.imag()); return *this; } + +#if _LIBCPP_STD_VER >= 20 + // conforming extension: This is not observable other than through SFINAE on `u.operator==` + // This makes complex trivially equality comparable if _Tp is trivially equality comparable + bool operator==(const complex&) const = default; +#endif }; template<> class _LIBCPP_TEMPLATE_VIS complex; diff --git a/libcxx/include/forward_list b/libcxx/include/forward_list --- a/libcxx/include/forward_list +++ b/libcxx/include/forward_list @@ -382,6 +382,7 @@ return __t; } +#if _LIBCPP_STD_VER <= 20 friend _LIBCPP_INLINE_VISIBILITY bool operator==(const __forward_list_iterator& __x, const __forward_list_iterator& __y) @@ -390,6 +391,10 @@ bool operator!=(const __forward_list_iterator& __x, const __forward_list_iterator& __y) {return !(__x == __y);} +#else + // This makes __forward_list_iterator trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const __forward_list_iterator&) const = default; +#endif }; template @@ -466,6 +471,7 @@ return __t; } +#if _LIBCPP_STD_VER <= 20 friend _LIBCPP_INLINE_VISIBILITY bool operator==(const __forward_list_const_iterator& __x, const __forward_list_const_iterator& __y) @@ -474,6 +480,10 @@ bool operator!=(const __forward_list_const_iterator& __x, const __forward_list_const_iterator& __y) {return !(__x == __y);} +#else + // This makes __forward_list_iterator trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const __forward_list_const_iterator&) const = default; +#endif }; template diff --git a/libcxx/include/list b/libcxx/include/list --- a/libcxx/include/list +++ b/libcxx/include/list @@ -420,6 +420,7 @@ _LIBCPP_INLINE_VISIBILITY __list_iterator operator--(int) {__list_iterator __t(*this); --(*this); return __t;} +#if _LIBCPP_STD_VER <= 20 friend _LIBCPP_INLINE_VISIBILITY bool operator==(const __list_iterator& __x, const __list_iterator& __y) { @@ -428,6 +429,10 @@ friend _LIBCPP_INLINE_VISIBILITY bool operator!=(const __list_iterator& __x, const __list_iterator& __y) {return !(__x == __y);} +#else + // This makes __list_iterator trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const __list_iterator&) const = default; +#endif }; template @@ -535,6 +540,7 @@ _LIBCPP_INLINE_VISIBILITY __list_const_iterator operator--(int) {__list_const_iterator __t(*this); --(*this); return __t;} +#if _LIBCPP_STD_VER <= 20 friend _LIBCPP_INLINE_VISIBILITY bool operator==(const __list_const_iterator& __x, const __list_const_iterator& __y) { @@ -543,6 +549,10 @@ friend _LIBCPP_INLINE_VISIBILITY bool operator!=(const __list_const_iterator& __x, const __list_const_iterator& __y) {return !(__x == __y);} +#else + // This makes __list_const_iterator trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const __list_const_iterator&) const = default; +#endif }; template diff --git a/libcxx/include/tuple b/libcxx/include/tuple --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -393,6 +393,11 @@ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 _Hp& get() _NOEXCEPT {return __value_;} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Hp& get() const _NOEXCEPT {return __value_;} + +#if _LIBCPP_STD_VER >= 20 + // This makes __tuple_leaf trivially equality comparable if _Hp is trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const __tuple_leaf&) const requires (is_object_v<_Hp>) = default; +#endif }; template @@ -465,6 +470,11 @@ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 _Hp& get() _NOEXCEPT {return static_cast<_Hp&>(*this);} _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14 const _Hp& get() const _NOEXCEPT {return static_cast(*this);} + +#if _LIBCPP_STD_VER >= 20 + // This makes __tuple_leaf trivially equality comparable if _Hp is trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const __tuple_leaf&) const requires (is_object_v<_Hp>) = default; +#endif }; template @@ -554,6 +564,13 @@ { _VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast&>(__t))...); } + +#if _LIBCPP_STD_VER >= 20 + // This makes __tuple_impl trivially equality comparable if all _Tp are trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const __tuple_impl&) const + requires(__all...>::value) + = default; +# endif }; template @@ -1310,6 +1327,11 @@ __base_.swap(__t.__base_); } #endif // _LIBCPP_STD_VER >= 23 + +#if _LIBCPP_STD_VER >= 20 + // This makes tuple trivially equality comparable if all _Tp are trivially equality comparable + _LIBCPP_HIDE_FROM_ABI bool operator==(const tuple&) const requires (__all...>::value) = default; +#endif }; template <> @@ -1334,6 +1356,11 @@ #if _LIBCPP_STD_VER >= 23 _LIBCPP_HIDE_FROM_ABI constexpr void swap(const tuple&) const noexcept {} #endif + +#if _LIBCPP_STD_VER >= 20 +public: + bool operator==(const tuple&) const = default; +#endif }; #if _LIBCPP_STD_VER >= 23 diff --git a/libcxx/test/libcxx/types_are_trivially_equality_comparable.compile.pass.cpp b/libcxx/test/libcxx/types_are_trivially_equality_comparable.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/types_are_trivially_equality_comparable.compile.pass.cpp @@ -0,0 +1,58 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: clang +// UNSUPPORTED: clang-15, clang-16 +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// ADDITIONAL_COMPILE_FLAGS: -Wno-private-header + +#include <__iterator/wrap_iter.h> +#include <__type_traits/is_equality_comparable.h> + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test_macros.h" + +template +constexpr bool is_tec = std::__libcpp_is_trivially_equality_comparable::value; + +static_assert(is_tec>); +static_assert(is_tec>); +static_assert(is_tec::iterator>); +static_assert(is_tec::const_iterator>); +static_assert(is_tec::iterator>); +static_assert(is_tec::const_iterator>); +// These are currently false because of a compiler bug +// static_assert(is_tec>); +// static_assert(is_tec>); +// static_assert(is_tec>); +static_assert(is_tec>); + +#if TEST_STD_VER >= 23 +static_assert(is_tec>); +#endif + +// TODO: Make these types trivially equality comparable: +// - std::chrono types +// - std::coroutine_handle +// - std::filesystem::space_info +// - std::unique_ptr +// - iterator_t +// - iterator_t +// - iterator_t +// - error_code +// - error_condition +// - set, map, unordered_set, etc. iterators diff --git a/libcxx/test/std/language.support/support.exception/propagation/exception_ptr.pass.cpp b/libcxx/test/std/language.support/support.exception/propagation/exception_ptr.pass.cpp --- a/libcxx/test/std/language.support/support.exception/propagation/exception_ptr.pass.cpp +++ b/libcxx/test/std/language.support/support.exception/propagation/exception_ptr.pass.cpp @@ -22,17 +22,24 @@ { std::exception_ptr p; assert(p == nullptr); + assert(!(p != nullptr)); std::exception_ptr p2 = p; assert(nullptr == p); + assert(!(nullptr != p)); assert(!p); assert(p2 == p); + assert(!(p2 != p)); p2 = p; assert(p2 == p); + assert(!(p2 != p)); assert(p2 == nullptr); + assert(!(p2 != nullptr)); std::exception_ptr p3 = nullptr; assert(p3 == nullptr); + assert(!(p3 != nullptr)); p3 = nullptr; assert(p3 == nullptr); + assert(!(p3 != nullptr)); return 0; } diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.rel/eq.pass.cpp @@ -22,138 +22,144 @@ #include "test_macros.h" -int main(int, char**) -{ - { - typedef std::tuple<> T1; - typedef std::tuple<> T2; - const T1 t1; - const T2 t2; - assert(t1 == t2); - assert(!(t1 != t2)); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1); - const T2 t2(1.1); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1); - const T2 t2(1); - assert(t1 == t2); - assert(!(t1 != t2)); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2); - const T2 t2(1, 2); - assert(t1 == t2); - assert(!(t1 != t2)); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2); - const T2 t2(1, 3); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2); - const T2 t2(1.1, 2); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2); - const T2 t2(1.1, 3); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2, 3); - const T2 t2(1, 2, 3); - assert(t1 == t2); - assert(!(t1 != t2)); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2, 3); - const T2 t2(1.1, 2, 3); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2, 3); - const T2 t2(1, 3, 3); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2, 3); - const T2 t2(1, 2, 4); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2, 3); - const T2 t2(1, 3, 2); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2, 3); - const T2 t2(1.1, 2, 2); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2, 3); - const T2 t2(1.1, 3, 3); - assert(!(t1 == t2)); - assert(t1 != t2); - } - { - typedef std::tuple T1; - typedef std::tuple T2; - const T1 t1(1, 2, 3); - const T2 t2(1.1, 3, 2); - assert(!(t1 == t2)); - assert(t1 != t2); - } -#if TEST_STD_VER > 11 - { - typedef std::tuple T1; - typedef std::tuple T2; - constexpr T1 t1(1, 2, 3); - constexpr T2 t2(1.1, 3, 2); - static_assert(!(t1 == t2), ""); - static_assert(t1 != t2, ""); - } -#endif +TEST_CONSTEXPR_CXX14 bool test() { + { + typedef std::tuple<> T1; + typedef std::tuple<> T2; + const T1 t1; + const T2 t2; + assert(t1 == t2); + assert(!(t1 != t2)); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1); + const T2 t2(1.1); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1); + const T2 t2(1); + assert(t1 == t2); + assert(!(t1 != t2)); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2); + const T2 t2(1, 2); + assert(t1 == t2); + assert(!(t1 != t2)); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2); + const T2 t2(1, 3); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2); + const T2 t2(1.1, 2); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2); + const T2 t2(1.1, 3); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2, 3); + const T2 t2(1, 2, 3); + assert(t1 == t2); + assert(!(t1 != t2)); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2, 3); + const T2 t2(1.1, 2, 3); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2, 3); + const T2 t2(1, 3, 3); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2, 3); + const T2 t2(1, 2, 4); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2, 3); + const T2 t2(1, 3, 2); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2, 3); + const T2 t2(1.1, 2, 2); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2, 3); + const T2 t2(1.1, 3, 3); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + typedef std::tuple T1; + typedef std::tuple T2; + const T1 t1(1, 2, 3); + const T2 t2(1.1, 3, 2); + assert(!(t1 == t2)); + assert(t1 != t2); + } + { + int i = 1; + double d = 1.1; + std::tuple lhs(i, d); + i = 2; + d = 2.2; + std::tuple rhs(i, d); + assert(lhs == rhs); + assert(!(lhs != rhs)); + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); return 0; }