Skip to content

WIP [libc++]P2944R3: Constrained comparisions - variant and tuple #141396

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

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
7 changes: 7 additions & 0 deletions libcxx/include/tuple
Original file line number Diff line number Diff line change
Expand Up @@ -1161,9 +1161,16 @@ struct __tuple_equal<0> {
};

template <class... _Tp, class... _Up>
# if _LIBCPP_STD_VER >= 26
requires(requires(const _Tp& __t, const _Up& __u) {
{ __t == __u } -> __boolean_testable;
} && ...) && (sizeof...(_Tp) == sizeof...(_Up))
# endif
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool
operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) {
# if _LIBCPP_STD_VER < 26
static_assert(sizeof...(_Tp) == sizeof...(_Up), "Can't compare tuples of different sizes");
# endif
return __tuple_equal<sizeof...(_Tp)>()(__x, __y);
}

Expand Down
31 changes: 31 additions & 0 deletions libcxx/include/variant
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ namespace std {
# include <__type_traits/is_assignable.h>
# include <__type_traits/is_constructible.h>
# include <__type_traits/is_convertible.h>
# include <__type_traits/is_core_convertible.h>
# include <__type_traits/is_destructible.h>
# include <__type_traits/is_nothrow_assignable.h>
# include <__type_traits/is_nothrow_constructible.h>
Expand Down Expand Up @@ -1453,6 +1454,11 @@ struct __convert_to_bool {
};

template <class... _Types>
# if _LIBCPP_STD_VER >= 26
requires(requires(const _Types& __t) {
{ __t == __t } -> __core_convertible_to<bool>;
} && ...)
# endif
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
using __variant_detail::__visitation::__variant;
if (__lhs.index() != __rhs.index())
Expand Down Expand Up @@ -1485,6 +1491,11 @@ operator<=>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
# endif // _LIBCPP_STD_VER >= 20

template <class... _Types>
# if _LIBCPP_STD_VER >= 26
requires(requires(const _Types& __t) {
{ __t != __t } -> __core_convertible_to<bool>;
} && ...)
# endif
_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
using __variant_detail::__visitation::__variant;
if (__lhs.index() != __rhs.index())
Expand All @@ -1495,6 +1506,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const variant<_Types...>& __lhs,
}

template <class... _Types>
# if _LIBCPP_STD_VER >= 26
requires(requires(const _Types& __t) {
{ __t < __t } -> __core_convertible_to<bool>;
} && ...)
# endif
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
using __variant_detail::__visitation::__variant;
if (__rhs.valueless_by_exception())
Expand All @@ -1509,6 +1525,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const variant<_Types...>& __lhs,
}

template <class... _Types>
# if _LIBCPP_STD_VER >= 26
requires(requires(const _Types& __t) {
{ __t > __t } -> __core_convertible_to<bool>;
} && ...)
# endif
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
using __variant_detail::__visitation::__variant;
if (__lhs.valueless_by_exception())
Expand All @@ -1523,6 +1544,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const variant<_Types...>& __lhs,
}

template <class... _Types>
# if _LIBCPP_STD_VER >= 26
requires(requires(const _Types& __t) {
{ __t <= __t } -> __core_convertible_to<bool>;
} && ...)
# endif
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
using __variant_detail::__visitation::__variant;
if (__lhs.valueless_by_exception())
Expand All @@ -1537,6 +1563,11 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const variant<_Types...>& __lhs,
}

template <class... _Types>
# if _LIBCPP_STD_VER >= 26
requires(requires(const _Types& __t) {
{ __t >= __t } -> __core_convertible_to<bool>;
} && ...)
# endif
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
using __variant_detail::__visitation::__variant;
if (__rhs.valueless_by_exception())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,31 @@

// UNSUPPORTED: c++03

#include <tuple>
#include <string>
#include <array>
#include <cassert>
#include <tuple>

#include "test_comparisons.h"
#include "test_macros.h"

#if TEST_STD_VER >= 26

// Test SFINAE.

static_assert(std::equality_comparable<std::tuple<EqualityComparable>>);
static_assert(std::equality_comparable<std::tuple<EqualityComparable, EqualityComparable>>);

static_assert(!std::equality_comparable<std::tuple<NonComparable>>);
static_assert(!std::equality_comparable<std::tuple<EqualityComparable, NonComparable>>);
static_assert(!std::equality_comparable<std::tuple<NonComparable, EqualityComparable>>);
static_assert(!std::equality_comparable_with<std::tuple<EqualityComparable>, std::tuple<NonComparable>>);
static_assert(!std::equality_comparable_with<std::tuple<NonComparable>, std::tuple<EqualityComparable>>);
// Size mismatch.
static_assert(!std::equality_comparable_with<std::tuple<EqualityComparable>, std::tuple<EqualityComparable, EqualityComparable>>);
static_assert(!std::equality_comparable_with<std::tuple<EqualityComparable, EqualityComparable>, std::tuple<EqualityComparable>>);

#endif

int main(int, char**)
{
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@

#include <tuple>

#include "test_macros.h"

#if TEST_STD_VER >= 26
// expected-no-diagnostics
#else
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@*:* {{}}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,57 @@
#include <utility>
#include <variant>

#include "test_comparisons.h"
#include "test_macros.h"

#if TEST_STD_VER >= 26

// Test SFINAE.

// ==
static_assert(HasOperatorEqual<std::variant<EqualityComparable>>);
static_assert(HasOperatorEqual<std::variant<EqualityComparable, int, long>>);

static_assert(!HasOperatorEqual<std::variant<NonComparable>>);
static_assert(!HasOperatorEqual<std::variant<NonComparable, EqualityComparable>>);

// >
static_assert(HasOperatorGreaterThan<std::variant<ThreeWayComparable>>);
static_assert(HasOperatorGreaterThan<std::variant<ThreeWayComparable, int, long>>);

static_assert(!HasOperatorGreaterThan<std::variant<NonComparable>>);
static_assert(!HasOperatorGreaterThan<std::variant<NonComparable, ThreeWayComparable>>);

// >=
static_assert(HasOperatorGreaterThanEqual<std::variant<ThreeWayComparable>>);
static_assert(HasOperatorGreaterThanEqual<std::variant<ThreeWayComparable, int, long>>);

static_assert(!HasOperatorGreaterThanEqual<std::variant<NonComparable>>);
static_assert(!HasOperatorGreaterThanEqual<std::variant<NonComparable, ThreeWayComparable>>);

// <
static_assert(HasOperatorLessThan<std::variant<ThreeWayComparable>>);
static_assert(HasOperatorLessThan<std::variant<ThreeWayComparable, int, long>>);

static_assert(!HasOperatorLessThan<std::variant<NonComparable>>);
static_assert(!HasOperatorLessThan<std::variant<NonComparable, ThreeWayComparable>>);

// <=
static_assert(HasOperatorLessThanEqual<std::variant<ThreeWayComparable>>);
static_assert(HasOperatorLessThanEqual<std::variant<ThreeWayComparable, int, long>>);

static_assert(!HasOperatorLessThanEqual<std::variant<NonComparable>>);
static_assert(!HasOperatorLessThanEqual<std::variant<NonComparable, ThreeWayComparable>>);

// !=
static_assert(HasOperatorNotEqual<std::variant<EqualityComparable>>);
static_assert(HasOperatorNotEqual<std::variant<EqualityComparable, int, long>>);

static_assert(!HasOperatorNotEqual<std::variant<NonComparable>>);
static_assert(!HasOperatorNotEqual<std::variant<NonComparable, EqualityComparable>>);

#endif

#ifndef TEST_HAS_NO_EXCEPTIONS
struct MakeEmptyT {
MakeEmptyT() = default;
Expand Down
66 changes: 65 additions & 1 deletion libcxx/test/support/test_comparisons.h
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,70 @@ struct PartialOrder {
}
};

#endif
template <typename T1, typename T2 = T1>
concept HasOperatorEqual = requires(T1 t1, T2 t2) { t1 == t2; };

template <typename T1, typename T2 = T1>
concept HasOperatorGreaterThan = requires(T1 t1, T2 t2) { t1 > t2; };

template <typename T1, typename T2 = T1>
concept HasOperatorGreaterThanEqual = requires(T1 t1, T2 t2) { t1 >= t2; };
template <typename T1, typename T2 = T1>
concept HasOperatorLessThan = requires(T1 t1, T2 t2) { t1 < t2; };

template <typename T1, typename T2 = T1>
concept HasOperatorLessThanEqual = requires(T1 t1, T2 t2) { t1 <= t2; };

template <typename T1, typename T2 = T1>
concept HasOperatorNotEqual = requires(T1 t1, T2 t2) { t1 != t2; };

template <typename T1, typename T2 = T1>
concept HasOperatorSpaceship = requires(T1 t1, T2 t2) { t1 <=> t2; };

struct NonComparable {};
static_assert(!std::equality_comparable<NonComparable>);
static_assert(!HasOperatorEqual<NonComparable>);
static_assert(!HasOperatorGreaterThan<NonComparable>);
static_assert(!HasOperatorGreaterThanEqual<NonComparable>);
static_assert(!HasOperatorLessThan<NonComparable>);
static_assert(!HasOperatorLessThanEqual<NonComparable>);
static_assert(!HasOperatorNotEqual<NonComparable>);
static_assert(!HasOperatorSpaceship<NonComparable>);

class EqualityComparable {
public:
constexpr EqualityComparable(int value) : value_{value} {};

friend constexpr bool operator==(const EqualityComparable&, const EqualityComparable&) noexcept = default;

private:
int value_;
};
static_assert(std::equality_comparable<EqualityComparable>);
static_assert(HasOperatorEqual<EqualityComparable>);
static_assert(HasOperatorNotEqual<EqualityComparable>);

class ThreeWayComparable {
public:
constexpr ThreeWayComparable(int value) : value_{value} {};

friend constexpr bool operator==(const ThreeWayComparable&, const ThreeWayComparable&) noexcept = default;
friend constexpr std::strong_ordering
operator<=>(const ThreeWayComparable&, const ThreeWayComparable&) noexcept = default;

private:
int value_;
};
static_assert(std::equality_comparable<ThreeWayComparable>);
static_assert(std::three_way_comparable<ThreeWayComparable>);
static_assert(HasOperatorEqual<ThreeWayComparable>);
static_assert(HasOperatorGreaterThan<ThreeWayComparable>);
static_assert(HasOperatorGreaterThanEqual<ThreeWayComparable>);
static_assert(HasOperatorLessThan<ThreeWayComparable>);
static_assert(HasOperatorLessThanEqual<ThreeWayComparable>);
static_assert(HasOperatorNotEqual<ThreeWayComparable>);
static_assert(HasOperatorSpaceship<ThreeWayComparable>);

#endif // TEST_STD_VER >= 20

#endif // TEST_COMPARISONS_H
Loading