[libc++][spaceship] Implement std::tuple::operator<=>
Implement parts of P1614, including three-way comparison for tuples, and expand testing. Reviewed By: ldionne, Mordante, #libc Differential Revision: https://reviews.llvm.org/D108250
This commit is contained in:
parent
955dc3449a
commit
b80f2dfd11
@ -14,7 +14,7 @@ Section,Description,Dependencies,Assignee,Complete
|
||||
| `[syserr.errcat.nonvirtuals] <https://wg21.link/syserr.errcat.nonvirtuals>`_,| error_category,[comparisons.three.way],Unassigned,|Not Started|
|
||||
| `[syserr.compare] <https://wg21.link/syserr.compare>`_,"| error_code
|
||||
| error_condition",None,Unassigned,|Not Started|
|
||||
| `[tuple.rel] <https://wg21.link/tuple.rel>`_,| `tuple <https://reviews.llvm.org/D108250>`_,[expos.only.func],Kent Ross,|In Progress|
|
||||
| `[tuple.rel] <https://wg21.link/tuple.rel>`_,| `tuple <https://reviews.llvm.org/D108250>`_,[expos.only.func],Kent Ross,|Complete|
|
||||
"| `[optional.relops] <https://wg21.link/optional.relops>`_
|
||||
| `[optional.nullops] <https://wg21.link/optional.nullops>`_
|
||||
| `[optional.comp.with.t] <https://wg21.link/optional.comp.with.t>`_","| optional
|
||||
|
||||
|
@ -132,11 +132,14 @@ template <class T1, class... T>
|
||||
|
||||
// 20.4.1.6, relational operators:
|
||||
template<class... T, class... U> bool operator==(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14
|
||||
template<class... T, class... U> bool operator<(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14
|
||||
template<class... T, class... U> bool operator!=(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14
|
||||
template<class... T, class... U> bool operator>(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14
|
||||
template<class... T, class... U> bool operator<=(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14
|
||||
template<class... T, class... U> bool operator>=(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14
|
||||
template<class... T, class... U> bool operator<(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14, removed in C++20
|
||||
template<class... T, class... U> bool operator!=(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14, removed in C++20
|
||||
template<class... T, class... U> bool operator>(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14, removed in C++20
|
||||
template<class... T, class... U> bool operator<=(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14, removed in C++20
|
||||
template<class... T, class... U> bool operator>=(const tuple<T...>&, const tuple<U...>&); // constexpr in C++14, removed in C++20
|
||||
template<class... T, class... U>
|
||||
constexpr common_comparison_category_t<synth-three-way-result<T, U>...>
|
||||
operator<=>(const tuple<T...>&, const tuple<U...>&); // since C++20
|
||||
|
||||
template <class... Types, class Alloc>
|
||||
struct uses_allocator<tuple<Types...>, Alloc>;
|
||||
@ -149,6 +152,8 @@ template <class... Types>
|
||||
|
||||
*/
|
||||
|
||||
#include <__compare/common_comparison_category.h>
|
||||
#include <__compare/synth_three_way.h>
|
||||
#include <__config>
|
||||
#include <__functional/unwrap_ref.h>
|
||||
#include <__functional_base>
|
||||
@ -156,6 +161,7 @@ template <class... Types>
|
||||
#include <__memory/uses_allocator.h>
|
||||
#include <__tuple>
|
||||
#include <__utility/forward.h>
|
||||
#include <__utility/integer_sequence.h>
|
||||
#include <__utility/move.h>
|
||||
#include <compare>
|
||||
#include <cstddef>
|
||||
@ -1300,6 +1306,30 @@ operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
|
||||
return __tuple_equal<sizeof...(_Tp)>()(__x, __y);
|
||||
}
|
||||
|
||||
#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
// operator<=>
|
||||
|
||||
template <class ..._Tp, class ..._Up, size_t ..._Is>
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr
|
||||
auto
|
||||
__tuple_compare_three_way(const tuple<_Tp...>& __x, const tuple<_Up...>& __y, index_sequence<_Is...>) {
|
||||
common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...> __result = strong_ordering::equal;
|
||||
static_cast<void>(((__result = _VSTD::__synth_three_way(_VSTD::get<_Is>(__x), _VSTD::get<_Is>(__y)), __result != 0) || ...));
|
||||
return __result;
|
||||
}
|
||||
|
||||
template <class ..._Tp, class ..._Up>
|
||||
requires (sizeof...(_Tp) == sizeof...(_Up))
|
||||
_LIBCPP_HIDE_FROM_ABI constexpr
|
||||
common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...>
|
||||
operator<=>(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
|
||||
{
|
||||
return _VSTD::__tuple_compare_three_way(__x, __y, index_sequence_for<_Tp...>{});
|
||||
}
|
||||
|
||||
#else // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
template <class ..._Tp, class ..._Up>
|
||||
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
|
||||
bool
|
||||
@ -1368,6 +1398,8 @@ operator<=(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
|
||||
return !(__y < __x);
|
||||
}
|
||||
|
||||
#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS)
|
||||
|
||||
// tuple_cat
|
||||
|
||||
template <class _Tp, class _Up> struct __tuple_cat_type;
|
||||
|
||||
@ -1,166 +0,0 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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, libcpp-no-concepts
|
||||
// ADDITIONAL_COMPILE_FLAGS: -Wno-sign-compare
|
||||
|
||||
// constexpr auto __synth_three_way = ...;
|
||||
|
||||
#include <cassert>
|
||||
#include <compare>
|
||||
#include <limits>
|
||||
#include <utility> // Includes synth-three-way via std::pair::operator<=>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
template <typename T> concept can_synth_three_way = requires(T t) { std::__synth_three_way(t, t); };
|
||||
|
||||
// A custom three-way result type
|
||||
struct CustomEquality {
|
||||
friend constexpr bool operator==(const CustomEquality&, int) noexcept { return true; }
|
||||
friend constexpr bool operator<(const CustomEquality&, int) noexcept { return false; }
|
||||
friend constexpr bool operator<(int, const CustomEquality&) noexcept { return false; }
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
assert(std::__synth_three_way(1, 1) == std::strong_ordering::equal);
|
||||
assert(std::__synth_three_way(2, 1) == std::strong_ordering::greater);
|
||||
assert(std::__synth_three_way(1, 2) == std::strong_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::strong_ordering, std::__synth_three_way_result<int, int>);
|
||||
ASSERT_SAME_TYPE(std::strong_ordering, std::__synth_three_way_result<short, long long int>);
|
||||
}
|
||||
{
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
assert(std::__synth_three_way(1.0, 1.0) == std::partial_ordering::equivalent);
|
||||
assert(std::__synth_three_way(2.0, 1.0) == std::partial_ordering::greater);
|
||||
assert(std::__synth_three_way(1.0, 2.0) == std::partial_ordering::less);
|
||||
assert(std::__synth_three_way(nan, nan) == std::partial_ordering::unordered);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, std::__synth_three_way_result<double, double>);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, std::__synth_three_way_result<double, float>);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, std::__synth_three_way_result<double, int>);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, std::__synth_three_way_result<float, short>);
|
||||
}
|
||||
{
|
||||
struct StrongSpaceship {
|
||||
int value;
|
||||
constexpr bool operator==(const StrongSpaceship&) const = default;
|
||||
constexpr std::strong_ordering operator<=>(const StrongSpaceship& other) const { return value <=> other.value; }
|
||||
};
|
||||
assert(std::__synth_three_way(StrongSpaceship{1}, StrongSpaceship{1}) == std::strong_ordering::equal);
|
||||
assert(std::__synth_three_way(StrongSpaceship{2}, StrongSpaceship{1}) == std::strong_ordering::greater);
|
||||
assert(std::__synth_three_way(StrongSpaceship{1}, StrongSpaceship{2}) == std::strong_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::strong_ordering, std::__synth_three_way_result<StrongSpaceship, StrongSpaceship>);
|
||||
}
|
||||
{
|
||||
struct WeakSpaceship {
|
||||
int value;
|
||||
constexpr bool operator==(const WeakSpaceship&) const = default;
|
||||
constexpr std::weak_ordering operator<=>(const WeakSpaceship& other) const {
|
||||
return value <=> other.value;
|
||||
}
|
||||
};
|
||||
assert(std::__synth_three_way(WeakSpaceship{1}, WeakSpaceship{1}) == std::weak_ordering::equivalent);
|
||||
assert(std::__synth_three_way(WeakSpaceship{2}, WeakSpaceship{1}) == std::weak_ordering::greater);
|
||||
assert(std::__synth_three_way(WeakSpaceship{1}, WeakSpaceship{2}) == std::weak_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, std::__synth_three_way_result<WeakSpaceship, WeakSpaceship>);
|
||||
}
|
||||
{
|
||||
struct PartialSpaceship {
|
||||
double value;
|
||||
constexpr bool operator==(const PartialSpaceship&) const = default;
|
||||
constexpr std::partial_ordering operator<=>(const PartialSpaceship& other) const {
|
||||
return value <=> other.value;
|
||||
}
|
||||
};
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
assert(std::__synth_three_way(PartialSpaceship{1.0}, PartialSpaceship{1.0}) == std::partial_ordering::equivalent);
|
||||
assert(std::__synth_three_way(PartialSpaceship{2.0}, PartialSpaceship{1.0}) == std::partial_ordering::greater);
|
||||
assert(std::__synth_three_way(PartialSpaceship{1.0}, PartialSpaceship{2.0}) == std::partial_ordering::less);
|
||||
assert(std::__synth_three_way(PartialSpaceship{nan}, PartialSpaceship{nan}) == std::partial_ordering::unordered);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, std::__synth_three_way_result<PartialSpaceship, PartialSpaceship>);
|
||||
}
|
||||
{
|
||||
struct NoSpaceship {
|
||||
int value;
|
||||
constexpr bool operator==(const NoSpaceship&) const = default;
|
||||
constexpr bool operator<(const NoSpaceship& other) const { return value < other.value; }
|
||||
};
|
||||
assert(std::__synth_three_way(NoSpaceship{1}, NoSpaceship{1}) == std::weak_ordering::equivalent);
|
||||
assert(std::__synth_three_way(NoSpaceship{2}, NoSpaceship{1}) == std::weak_ordering::greater);
|
||||
assert(std::__synth_three_way(NoSpaceship{1}, NoSpaceship{2}) == std::weak_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, std::__synth_three_way_result<NoSpaceship, NoSpaceship>);
|
||||
}
|
||||
{
|
||||
// Types with operator<=> but no operator== are not three_way_comparable and will fall back to operator< and
|
||||
// compare as weakly ordered.
|
||||
struct SpaceshipNoEquals {
|
||||
constexpr std::strong_ordering operator<=>(const SpaceshipNoEquals&) const {
|
||||
return std::strong_ordering::equivalent;
|
||||
}
|
||||
};
|
||||
assert(std::__synth_three_way(SpaceshipNoEquals{}, SpaceshipNoEquals{}) == std::weak_ordering::equivalent);
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, std::__synth_three_way_result<SpaceshipNoEquals, SpaceshipNoEquals>);
|
||||
}
|
||||
{
|
||||
// Custom three-way-comparison result types cannot satisfy standard concepts (and therefore synth-three-way)
|
||||
// because they are not understood by std::common_comparison_category, but they can still be used in
|
||||
// the same way as standard orderings to do comparisons, and thus can be used by synth-three-way to yield a
|
||||
// weakly-ordered result.
|
||||
struct CustomSpaceship {
|
||||
constexpr CustomEquality operator<=>(const CustomSpaceship&) const { return CustomEquality(); }
|
||||
};
|
||||
assert((CustomSpaceship() <=> CustomSpaceship()) == 0);
|
||||
assert(!(CustomSpaceship() < CustomSpaceship()));
|
||||
assert(std::__synth_three_way(CustomSpaceship(), CustomSpaceship()) == std::weak_ordering::equivalent);
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, std::__synth_three_way_result<CustomSpaceship, CustomSpaceship>);
|
||||
}
|
||||
// SFINAE tests demonstrating synth-three-way needs three_way_comparable or operator<.
|
||||
{
|
||||
struct NoRelative {
|
||||
constexpr bool operator==(const NoRelative&) const;
|
||||
};
|
||||
static_assert(!can_synth_three_way<NoRelative>);
|
||||
}
|
||||
{
|
||||
struct NoLessThan {
|
||||
constexpr bool operator==(const NoLessThan&) const;
|
||||
constexpr bool operator>(const NoLessThan&) const;
|
||||
constexpr bool operator>=(const NoLessThan&) const;
|
||||
constexpr bool operator<=(const NoLessThan&) const;
|
||||
};
|
||||
static_assert(!can_synth_three_way<NoLessThan>);
|
||||
}
|
||||
{
|
||||
assert(std::__synth_three_way(1, 1U) == std::weak_ordering::equivalent);
|
||||
assert(std::__synth_three_way(-1, 0U) == std::weak_ordering::greater);
|
||||
// Even with the warning suppressed (-Wno-sign-compare) there should still be no <=> operator
|
||||
// between signed and unsigned types, so we should end up with a synthesized weak ordering.
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, std::__synth_three_way_result<int, unsigned int>);
|
||||
// When an unsigned type can be narrowed to a larger signed type, <=> should be defined and we
|
||||
// should get a strong ordering. (This probably does not raise a warning due to safe narrowing.)
|
||||
assert((static_cast<long long int>(-1) <=> static_cast<unsigned char>(0)) == std::strong_ordering::less);
|
||||
assert(std::__synth_three_way(static_cast<long long int>(-1),
|
||||
static_cast<unsigned char>(0)) == std::strong_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::strong_ordering, std::__synth_three_way_result<long long int, unsigned char>);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
{
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
assert(std::__synth_three_way(nan, 1.0) == std::partial_ordering::unordered);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,179 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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, libcpp-no-concepts
|
||||
// ADDITIONAL_COMPILE_FLAGS: -Wno-sign-compare
|
||||
|
||||
// constexpr auto synth-three-way = ...;
|
||||
// via std::tuple<T>(t) <=> std::tuple<U>(u), which exposes its behavior most directly
|
||||
|
||||
#include <cassert>
|
||||
#include <compare>
|
||||
#include <limits> // quiet_NaN
|
||||
#include <tuple>
|
||||
#include <utility> // declval
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
template <typename T, typename U = T>
|
||||
concept can_synth_three_way = requires(T t, U u) { std::tuple<T>(t) <=> std::tuple<U>(u); };
|
||||
|
||||
template <typename T, typename U>
|
||||
constexpr auto synth_three_way(const T& t, const U& u) {
|
||||
return std::tuple<T>(t) <=> std::tuple<U>(u);
|
||||
}
|
||||
|
||||
template <typename T, typename U>
|
||||
using synth_three_way_result = decltype(std::declval<std::tuple<T>>() <=> std::declval<std::tuple<U>>());
|
||||
|
||||
// A custom three-way result type
|
||||
struct CustomEquality {
|
||||
friend constexpr bool operator==(const CustomEquality&, int) noexcept { return true; }
|
||||
friend constexpr bool operator<(const CustomEquality&, int) noexcept { return false; }
|
||||
friend constexpr bool operator<(int, const CustomEquality&) noexcept { return false; }
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
{
|
||||
assert(synth_three_way(1, 1) == std::strong_ordering::equal);
|
||||
assert(synth_three_way(2, 1) == std::strong_ordering::greater);
|
||||
assert(synth_three_way(1, 2) == std::strong_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<int, int>);
|
||||
ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<short, long long int>);
|
||||
}
|
||||
{
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
assert(synth_three_way(1.0, 1.0) == std::partial_ordering::equivalent);
|
||||
assert(synth_three_way(2.0, 1.0) == std::partial_ordering::greater);
|
||||
assert(synth_three_way(1.0, 2.0) == std::partial_ordering::less);
|
||||
assert(synth_three_way(nan, nan) == std::partial_ordering::unordered);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, double>);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, float>);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, int>);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<float, short>);
|
||||
}
|
||||
{
|
||||
struct StrongSpaceship {
|
||||
int value;
|
||||
constexpr bool operator==(const StrongSpaceship&) const = default;
|
||||
constexpr std::strong_ordering operator<=>(const StrongSpaceship& other) const { return value <=> other.value; }
|
||||
};
|
||||
assert(synth_three_way(StrongSpaceship{1}, StrongSpaceship{1}) == std::strong_ordering::equal);
|
||||
assert(synth_three_way(StrongSpaceship{2}, StrongSpaceship{1}) == std::strong_ordering::greater);
|
||||
assert(synth_three_way(StrongSpaceship{1}, StrongSpaceship{2}) == std::strong_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<StrongSpaceship, StrongSpaceship>);
|
||||
}
|
||||
{
|
||||
struct WeakSpaceship {
|
||||
int value;
|
||||
constexpr bool operator==(const WeakSpaceship&) const = default;
|
||||
constexpr std::weak_ordering operator<=>(const WeakSpaceship& other) const {
|
||||
return value <=> other.value;
|
||||
}
|
||||
};
|
||||
assert(synth_three_way(WeakSpaceship{1}, WeakSpaceship{1}) == std::weak_ordering::equivalent);
|
||||
assert(synth_three_way(WeakSpaceship{2}, WeakSpaceship{1}) == std::weak_ordering::greater);
|
||||
assert(synth_three_way(WeakSpaceship{1}, WeakSpaceship{2}) == std::weak_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<WeakSpaceship, WeakSpaceship>);
|
||||
}
|
||||
{
|
||||
struct PartialSpaceship {
|
||||
double value;
|
||||
constexpr bool operator==(const PartialSpaceship&) const = default;
|
||||
constexpr std::partial_ordering operator<=>(const PartialSpaceship& other) const {
|
||||
return value <=> other.value;
|
||||
}
|
||||
};
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
assert(synth_three_way(PartialSpaceship{1.0}, PartialSpaceship{1.0}) == std::partial_ordering::equivalent);
|
||||
assert(synth_three_way(PartialSpaceship{2.0}, PartialSpaceship{1.0}) == std::partial_ordering::greater);
|
||||
assert(synth_three_way(PartialSpaceship{1.0}, PartialSpaceship{2.0}) == std::partial_ordering::less);
|
||||
assert(synth_three_way(PartialSpaceship{nan}, PartialSpaceship{nan}) == std::partial_ordering::unordered);
|
||||
ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<PartialSpaceship, PartialSpaceship>);
|
||||
}
|
||||
{
|
||||
struct NoSpaceship {
|
||||
int value;
|
||||
constexpr bool operator==(const NoSpaceship&) const = default;
|
||||
constexpr bool operator<(const NoSpaceship& other) const { return value < other.value; }
|
||||
};
|
||||
assert(synth_three_way(NoSpaceship{1}, NoSpaceship{1}) == std::weak_ordering::equivalent);
|
||||
assert(synth_three_way(NoSpaceship{2}, NoSpaceship{1}) == std::weak_ordering::greater);
|
||||
assert(synth_three_way(NoSpaceship{1}, NoSpaceship{2}) == std::weak_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<NoSpaceship, NoSpaceship>);
|
||||
}
|
||||
{
|
||||
// Types with operator<=> but no operator== are not three_way_comparable and will fall back to operator< and
|
||||
// compare as weakly ordered.
|
||||
struct SpaceshipNoEquals {
|
||||
constexpr std::strong_ordering operator<=>(const SpaceshipNoEquals&) const {
|
||||
return std::strong_ordering::equivalent;
|
||||
}
|
||||
};
|
||||
assert(synth_three_way(SpaceshipNoEquals{}, SpaceshipNoEquals{}) == std::weak_ordering::equivalent);
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<SpaceshipNoEquals, SpaceshipNoEquals>);
|
||||
}
|
||||
{
|
||||
// Custom three-way-comparison result types cannot satisfy standard concepts (and therefore synth-three-way)
|
||||
// because they are not understood by std::common_comparison_category, but they can still be used in
|
||||
// the same way as standard orderings to do comparisons, and thus can be used by synth-three-way to yield a
|
||||
// weakly-ordered result.
|
||||
struct CustomSpaceship {
|
||||
constexpr CustomEquality operator<=>(const CustomSpaceship&) const { return CustomEquality(); }
|
||||
};
|
||||
assert((CustomSpaceship{} <=> CustomSpaceship{}) == 0);
|
||||
assert(!(CustomSpaceship{} < CustomSpaceship{}));
|
||||
assert(synth_three_way(CustomSpaceship{}, CustomSpaceship{}) == std::weak_ordering::equivalent);
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<CustomSpaceship, CustomSpaceship>);
|
||||
}
|
||||
// SFINAE tests demonstrating synth-three-way needs three_way_comparable or operator<.
|
||||
{
|
||||
struct NoRelative {
|
||||
constexpr bool operator==(const NoRelative&) const;
|
||||
};
|
||||
static_assert(!can_synth_three_way<NoRelative>);
|
||||
}
|
||||
{
|
||||
struct NoLessThan {
|
||||
constexpr bool operator==(const NoLessThan&) const;
|
||||
constexpr bool operator>(const NoLessThan&) const;
|
||||
constexpr bool operator>=(const NoLessThan&) const;
|
||||
constexpr bool operator<=(const NoLessThan&) const;
|
||||
};
|
||||
static_assert(!can_synth_three_way<NoLessThan>);
|
||||
}
|
||||
{
|
||||
assert(synth_three_way(1, 1U) == std::weak_ordering::equivalent);
|
||||
assert(synth_three_way(-1, 0U) == std::weak_ordering::greater);
|
||||
// Even with the warning suppressed (-Wno-sign-compare) there should still be no <=> operator
|
||||
// between signed and unsigned types, so we should end up with a synthesized weak ordering.
|
||||
ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<int, unsigned int>);
|
||||
// When an unsigned type can be narrowed to a larger signed type, <=> should be defined and we
|
||||
// should get a strong ordering. (This probably does not raise a warning due to safe narrowing.)
|
||||
assert(synth_three_way(static_cast<long long int>(-1), static_cast<unsigned char>(0)) == std::strong_ordering::less);
|
||||
assert(synth_three_way(static_cast<long long int>(-1), static_cast<unsigned char>(0)) == std::strong_ordering::less);
|
||||
ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<long long int, unsigned char>);
|
||||
}
|
||||
#ifdef TEST_COMPILER_GCC
|
||||
// GCC cannot evaluate NaN @ non-NaN constexpr, so test that runtime-only.
|
||||
if (!std::is_constant_evaluated())
|
||||
#endif
|
||||
{
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
assert(synth_three_way(nan, 1.0) == std::partial_ordering::unordered);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <tuple>
|
||||
|
||||
// template <class... Types> class tuple;
|
||||
|
||||
// template<class... TTypes, class... UTypes>
|
||||
// bool
|
||||
// operator==(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
|
||||
// template<class... TTypes, class... UTypes>
|
||||
// bool
|
||||
// operator<(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
|
||||
|
||||
// UNSUPPORTED: c++03
|
||||
|
||||
#include <tuple>
|
||||
|
||||
void f(std::tuple<int> t1, std::tuple<int, long> t2) {
|
||||
// We test only the core comparison operators and trust that the others
|
||||
// fall back on the same implementations prior to C++20.
|
||||
static_cast<void>(t1 == t2); // expected-error@*:* {{}}
|
||||
static_cast<void>(t1 < t2); // expected-error@*:* {{}}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <tuple>
|
||||
|
||||
// template <class... Types> class tuple;
|
||||
|
||||
// template<class... TTypes, class... UTypes>
|
||||
// auto
|
||||
// operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, libcpp-no-concepts
|
||||
|
||||
#include <tuple>
|
||||
|
||||
template <class T, class U>
|
||||
concept can_compare = requires(T t, U u) { t <=> u; };
|
||||
|
||||
typedef std::tuple<int> T1;
|
||||
typedef std::tuple<int, long> T2;
|
||||
|
||||
static_assert(!can_compare<T1, T2>);
|
||||
static_assert(!can_compare<T2, T1>);
|
||||
@ -0,0 +1,232 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// 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
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// <tuple>
|
||||
|
||||
// template <class... Types> class tuple;
|
||||
|
||||
// template<class... TTypes, class... UTypes>
|
||||
// auto
|
||||
// operator<=>(const tuple<TTypes...>& t, const tuple<UTypes...>& u);
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17, libcpp-no-concepts
|
||||
// ADDITIONAL_COMPILE_FLAGS: -Wno-sign-compare
|
||||
|
||||
#include <cassert>
|
||||
#include <compare>
|
||||
#include <limits>
|
||||
#include <tuple>
|
||||
#include <type_traits> // std::is_constant_evaluated
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
// A custom three-way result type
|
||||
struct CustomEquality {
|
||||
friend constexpr bool operator==(const CustomEquality&, int) noexcept { return true; }
|
||||
friend constexpr bool operator<(const CustomEquality&, int) noexcept { return false; }
|
||||
friend constexpr bool operator<(int, const CustomEquality&) noexcept { return false; }
|
||||
};
|
||||
|
||||
constexpr bool test() {
|
||||
// Empty tuple
|
||||
{
|
||||
typedef std::tuple<> T0;
|
||||
// No member types yields strong ordering (all are equal).
|
||||
ASSERT_SAME_TYPE(decltype(T0() <=> T0()), std::strong_ordering);
|
||||
assert((T0() <=> T0()) == std::strong_ordering::equal);
|
||||
}
|
||||
// Mixed types with integers, which compare strongly ordered
|
||||
{
|
||||
typedef std::tuple<long> T1;
|
||||
typedef std::tuple<short> T2;
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering);
|
||||
assert((T1(1) <=> T2(1)) == std::strong_ordering::equal);
|
||||
assert((T1(1) <=> T2(0)) == std::strong_ordering::greater);
|
||||
assert((T1(1) <=> T2(2)) == std::strong_ordering::less);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<long, unsigned int> T1;
|
||||
typedef std::tuple<short, unsigned long> T2;
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering);
|
||||
assert((T1(1, 2) <=> T2(1, 2)) == std::strong_ordering::equal);
|
||||
assert((T1(1, 2) <=> T2(0, 2)) == std::strong_ordering::greater);
|
||||
assert((T1(1, 2) <=> T2(2, 2)) == std::strong_ordering::less);
|
||||
assert((T1(1, 2) <=> T2(1, 1)) == std::strong_ordering::greater);
|
||||
assert((T1(1, 2) <=> T2(1, 3)) == std::strong_ordering::less);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<long, int, unsigned short> T1;
|
||||
typedef std::tuple<short, long, unsigned int> T2;
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 2, 3)) == std::strong_ordering::equal);
|
||||
assert((T1(1, 2, 3) <=> T2(0, 2, 3)) == std::strong_ordering::greater);
|
||||
assert((T1(1, 2, 3) <=> T2(2, 2, 3)) == std::strong_ordering::less);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 1, 3)) == std::strong_ordering::greater);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 3, 3)) == std::strong_ordering::less);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 2, 2)) == std::strong_ordering::greater);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 2, 4)) == std::strong_ordering::less);
|
||||
}
|
||||
// Mixed types with floating point, which compare partially ordered
|
||||
{
|
||||
typedef std::tuple<long> T1;
|
||||
typedef std::tuple<double> T2;
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
|
||||
assert((T1(1) <=> T2(1)) == std::partial_ordering::equivalent);
|
||||
assert((T1(1) <=> T2(0.9)) == std::partial_ordering::greater);
|
||||
assert((T1(1) <=> T2(1.1)) == std::partial_ordering::less);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<long, float> T1;
|
||||
typedef std::tuple<double, unsigned int> T2;
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
|
||||
assert((T1(1, 2) <=> T2(1, 2)) == std::partial_ordering::equivalent);
|
||||
assert((T1(1, 2) <=> T2(0.9, 2)) == std::partial_ordering::greater);
|
||||
assert((T1(1, 2) <=> T2(1.1, 2)) == std::partial_ordering::less);
|
||||
assert((T1(1, 2) <=> T2(1, 1)) == std::partial_ordering::greater);
|
||||
assert((T1(1, 2) <=> T2(1, 3)) == std::partial_ordering::less);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<short, float, double> T1;
|
||||
typedef std::tuple<double, long, unsigned int> T2;
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 2, 3)) == std::partial_ordering::equivalent);
|
||||
assert((T1(1, 2, 3) <=> T2(0.9, 2, 3)) == std::partial_ordering::greater);
|
||||
assert((T1(1, 2, 3) <=> T2(1.1, 2, 3)) == std::partial_ordering::less);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 1, 3)) == std::partial_ordering::greater);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 3, 3)) == std::partial_ordering::less);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 2, 2)) == std::partial_ordering::greater);
|
||||
assert((T1(1, 2, 3) <=> T2(1, 2, 4)) == std::partial_ordering::less);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<float> T1;
|
||||
typedef std::tuple<double> T2;
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
// Comparisons with NaN and non-NaN are non-constexpr in GCC, so both sides must be NaN
|
||||
assert((T1(nan) <=> T2(nan)) == std::partial_ordering::unordered);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<double, double> T1;
|
||||
typedef std::tuple<float, float> T2;
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
assert((T1(nan, 2) <=> T2(nan, 2)) == std::partial_ordering::unordered);
|
||||
assert((T1(1, nan) <=> T2(1, nan)) == std::partial_ordering::unordered);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<double, float, float> T1;
|
||||
typedef std::tuple<double, double, float> T2;
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
assert((T1(nan, 2, 3) <=> T2(nan, 2, 3)) == std::partial_ordering::unordered);
|
||||
assert((T1(1, nan, 3) <=> T2(1, nan, 3)) == std::partial_ordering::unordered);
|
||||
assert((T1(1, 2, nan) <=> T2(1, 2, nan)) == std::partial_ordering::unordered);
|
||||
}
|
||||
// Ordering classes and synthesized three way comparison
|
||||
{
|
||||
typedef std::tuple<long, int, unsigned int> T1;
|
||||
typedef std::tuple<int, long, unsigned short> T2;
|
||||
// All strongly ordered members yields strong ordering.
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::strong_ordering);
|
||||
}
|
||||
{
|
||||
struct WeakSpaceship {
|
||||
constexpr bool operator==(const WeakSpaceship&) const { return true; }
|
||||
constexpr std::weak_ordering operator<=>(const WeakSpaceship&) const { return std::weak_ordering::equivalent; }
|
||||
};
|
||||
{
|
||||
typedef std::tuple<int, unsigned int, WeakSpaceship> T1;
|
||||
typedef std::tuple<int, unsigned long, WeakSpaceship> T2;
|
||||
// Strongly ordered members and a weakly ordered member yields weak ordering.
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<unsigned int, int, WeakSpaceship> T1;
|
||||
typedef std::tuple<double, long, WeakSpaceship> T2;
|
||||
// Doubles are partially ordered, so one partial, one strong, and one weak ordering
|
||||
// yields partial ordering.
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
|
||||
}
|
||||
}
|
||||
{
|
||||
struct NoSpaceship {
|
||||
constexpr bool operator==(const NoSpaceship&) const { return true; }
|
||||
constexpr bool operator<(const NoSpaceship&) const { return false; }
|
||||
};
|
||||
typedef std::tuple<int, unsigned int, NoSpaceship> T1;
|
||||
typedef std::tuple<int, unsigned long, NoSpaceship> T2;
|
||||
// Strongly ordered members and a weakly ordered member (synthesized) yields weak ordering.
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
|
||||
}
|
||||
{
|
||||
struct SpaceshipNoEquals {
|
||||
constexpr std::strong_ordering operator<=>(const SpaceshipNoEquals&) const { return std::strong_ordering::equal; }
|
||||
constexpr bool operator<(const SpaceshipNoEquals&) const { return false; }
|
||||
};
|
||||
typedef std::tuple<int, unsigned int, SpaceshipNoEquals> T1;
|
||||
typedef std::tuple<int, unsigned long, SpaceshipNoEquals> T2;
|
||||
// Spaceship operator with no == operator falls back on the < operator and weak ordering.
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
|
||||
}
|
||||
{
|
||||
struct CustomSpaceship {
|
||||
constexpr CustomEquality operator<=>(const CustomSpaceship&) const { return CustomEquality(); }
|
||||
};
|
||||
typedef std::tuple<int, unsigned int, CustomSpaceship> T1;
|
||||
typedef std::tuple<short, unsigned long, CustomSpaceship> T2;
|
||||
typedef std::tuple<CustomSpaceship> T3;
|
||||
// Custom three way return types cannot be used in synthesized three way comparison,
|
||||
// but they can be used for (rewritten) operator< when synthesizing a weak ordering.
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
|
||||
ASSERT_SAME_TYPE(decltype(T3() <=> T3()), std::weak_ordering);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<long, int> T1;
|
||||
typedef std::tuple<long, unsigned int> T2;
|
||||
// Even with the warning suppressed (-Wno-sign-compare) there should still be no <=> operator
|
||||
// between signed and unsigned types, so we should end up with a synthesized weak ordering.
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::weak_ordering);
|
||||
}
|
||||
|
||||
#ifdef TEST_COMPILER_GCC
|
||||
// GCC cannot evaluate NaN @ non-NaN constexpr, so test that runtime-only.
|
||||
if (!std::is_constant_evaluated())
|
||||
#endif
|
||||
{
|
||||
{
|
||||
typedef std::tuple<double> T1;
|
||||
typedef std::tuple<int> T2;
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
|
||||
assert((T1(nan) <=> T2(1)) == std::partial_ordering::unordered);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<double, double> T1;
|
||||
typedef std::tuple<int, int> T2;
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
|
||||
assert((T1(nan, 2) <=> T2(1, 2)) == std::partial_ordering::unordered);
|
||||
assert((T1(1, nan) <=> T2(1, 2)) == std::partial_ordering::unordered);
|
||||
}
|
||||
{
|
||||
typedef std::tuple<double, double, double> T1;
|
||||
typedef std::tuple<int, int, int> T2;
|
||||
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
||||
ASSERT_SAME_TYPE(decltype(T1() <=> T2()), std::partial_ordering);
|
||||
assert((T1(nan, 2, 3) <=> T2(1, 2, 3)) == std::partial_ordering::unordered);
|
||||
assert((T1(1, nan, 3) <=> T2(1, 2, 3)) == std::partial_ordering::unordered);
|
||||
assert((T1(1, 2, nan) <=> T2(1, 2, 3)) == std::partial_ordering::unordered);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**) {
|
||||
test();
|
||||
static_assert(test());
|
||||
|
||||
return 0;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user