mirror of
https://github.com/llvm/llvm-project.git
synced 2025-04-30 09:26:06 +00:00

`std::variant::operator<=>` is missing a requires clause ensuring that `operator<=>` only exists when all of the types in the variant are `three_way_comparable`. Add the missing requires clause and adjust the existing test which was incorrect. Fixes https://github.com/llvm/llvm-project/issues/58192. Differential Revision: https://reviews.llvm.org/D136050
195 lines
5.8 KiB
C++
195 lines
5.8 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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
|
|
|
|
// <variant>
|
|
|
|
// template <class... Types> class variant;
|
|
|
|
// template <class... Types> requires (three_way_comparable<Types> && ...)
|
|
// constexpr std::common_comparison_category_t<
|
|
// std::compare_three_way_result_t<Types>...>
|
|
// operator<=>(const variant<Types...>& t, const variant<Types...>& u);
|
|
|
|
#include <cassert>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <variant>
|
|
|
|
#include "test_macros.h"
|
|
#include "test_comparisons.h"
|
|
|
|
#ifndef TEST_HAS_NO_EXCEPTIONS
|
|
// MakeEmptyT throws in operator=(&&), so we can move to it to create valueless-by-exception variants.
|
|
struct MakeEmptyT {
|
|
MakeEmptyT() = default;
|
|
MakeEmptyT(MakeEmptyT&&) { throw 42; }
|
|
MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
|
|
};
|
|
inline bool operator==(const MakeEmptyT&, const MakeEmptyT&) {
|
|
assert(false);
|
|
return false;
|
|
}
|
|
inline std::weak_ordering operator<=>(const MakeEmptyT&, const MakeEmptyT&) {
|
|
assert(false);
|
|
return std::weak_ordering::equivalent;
|
|
}
|
|
|
|
template <class Variant>
|
|
void makeEmpty(Variant& v) {
|
|
Variant v2(std::in_place_type<MakeEmptyT>);
|
|
try {
|
|
v = std::move(v2);
|
|
assert(false);
|
|
} catch (...) {
|
|
assert(v.valueless_by_exception());
|
|
}
|
|
}
|
|
|
|
void test_empty() {
|
|
{
|
|
using V = std::variant<int, MakeEmptyT>;
|
|
V v1;
|
|
V v2;
|
|
makeEmpty(v2);
|
|
assert(testOrder(v1, v2, std::weak_ordering::greater));
|
|
}
|
|
{
|
|
using V = std::variant<int, MakeEmptyT>;
|
|
V v1;
|
|
makeEmpty(v1);
|
|
V v2;
|
|
assert(testOrder(v1, v2, std::weak_ordering::less));
|
|
}
|
|
{
|
|
using V = std::variant<int, MakeEmptyT>;
|
|
V v1;
|
|
makeEmpty(v1);
|
|
V v2;
|
|
makeEmpty(v2);
|
|
assert(testOrder(v1, v2, std::weak_ordering::equivalent));
|
|
}
|
|
}
|
|
#endif // TEST_HAS_NO_EXCEPTIONS
|
|
|
|
template <class T1, class T2, class Order>
|
|
constexpr bool test_with_types() {
|
|
using V = std::variant<T1, T2>;
|
|
AssertOrderReturn<Order, V>();
|
|
{ // same index, same value
|
|
constexpr V v1(std::in_place_index<0>, T1{1});
|
|
constexpr V v2(std::in_place_index<0>, T1{1});
|
|
assert(testOrder(v1, v2, Order::equivalent));
|
|
}
|
|
{ // same index, value < other_value
|
|
constexpr V v1(std::in_place_index<0>, T1{0});
|
|
constexpr V v2(std::in_place_index<0>, T1{1});
|
|
assert(testOrder(v1, v2, Order::less));
|
|
}
|
|
{ // same index, value > other_value
|
|
constexpr V v1(std::in_place_index<0>, T1{1});
|
|
constexpr V v2(std::in_place_index<0>, T1{0});
|
|
assert(testOrder(v1, v2, Order::greater));
|
|
}
|
|
{ // LHS.index() < RHS.index()
|
|
constexpr V v1(std::in_place_index<0>, T1{0});
|
|
constexpr V v2(std::in_place_index<1>, T2{0});
|
|
assert(testOrder(v1, v2, Order::less));
|
|
}
|
|
{ // LHS.index() > RHS.index()
|
|
constexpr V v1(std::in_place_index<1>, T2{0});
|
|
constexpr V v2(std::in_place_index<0>, T1{0});
|
|
assert(testOrder(v1, v2, Order::greater));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
constexpr bool test_three_way() {
|
|
assert((test_with_types<int, double, std::partial_ordering>()));
|
|
assert((test_with_types<int, long, std::strong_ordering>()));
|
|
|
|
{
|
|
using V = std::variant<int, double>;
|
|
constexpr double nan = std::numeric_limits<double>::quiet_NaN();
|
|
{
|
|
constexpr V v1(std::in_place_type<int>, 1);
|
|
constexpr V v2(std::in_place_type<double>, nan);
|
|
assert(testOrder(v1, v2, std::partial_ordering::less));
|
|
}
|
|
{
|
|
constexpr V v1(std::in_place_type<double>, nan);
|
|
constexpr V v2(std::in_place_type<int>, 2);
|
|
assert(testOrder(v1, v2, std::partial_ordering::greater));
|
|
}
|
|
{
|
|
constexpr V v1(std::in_place_type<double>, nan);
|
|
constexpr V v2(std::in_place_type<double>, nan);
|
|
assert(testOrder(v1, v2, std::partial_ordering::unordered));
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// SFINAE tests
|
|
template <class T, class U = T>
|
|
concept has_three_way_op = requires (T& t, U& u) { t <=> u; };
|
|
|
|
// std::three_way_comparable is a more stringent requirement that demands
|
|
// operator== and a few other things.
|
|
using std::three_way_comparable;
|
|
|
|
struct HasSimpleOrdering {
|
|
constexpr bool operator==(const HasSimpleOrdering&) const;
|
|
constexpr bool operator<(const HasSimpleOrdering&) const;
|
|
};
|
|
|
|
struct HasOnlySpaceship {
|
|
constexpr bool operator==(const HasOnlySpaceship&) const = delete;
|
|
constexpr std::weak_ordering operator<=>(const HasOnlySpaceship&) const;
|
|
};
|
|
|
|
struct HasFullOrdering {
|
|
constexpr bool operator==(const HasFullOrdering&) const;
|
|
constexpr std::weak_ordering operator<=>(const HasFullOrdering&) const;
|
|
};
|
|
|
|
// operator<=> must resolve the return types of all its union types'
|
|
// operator<=>s to determine its own return type, so it is detectable by SFINAE
|
|
static_assert(!has_three_way_op<HasSimpleOrdering>);
|
|
static_assert(!has_three_way_op<std::variant<int, HasSimpleOrdering>>);
|
|
|
|
static_assert(!three_way_comparable<HasSimpleOrdering>);
|
|
static_assert(!three_way_comparable<std::variant<int, HasSimpleOrdering>>);
|
|
|
|
static_assert(has_three_way_op<HasOnlySpaceship>);
|
|
static_assert(!has_three_way_op<std::variant<int, HasOnlySpaceship>>);
|
|
|
|
static_assert(!three_way_comparable<HasOnlySpaceship>);
|
|
static_assert(!three_way_comparable<std::variant<int, HasOnlySpaceship>>);
|
|
|
|
static_assert( has_three_way_op<HasFullOrdering>);
|
|
static_assert( has_three_way_op<std::variant<int, HasFullOrdering>>);
|
|
|
|
static_assert( three_way_comparable<HasFullOrdering>);
|
|
static_assert( three_way_comparable<std::variant<int, HasFullOrdering>>);
|
|
|
|
int main(int, char**) {
|
|
test_three_way();
|
|
static_assert(test_three_way());
|
|
|
|
#ifndef TEST_HAS_NO_EXCEPTIONS
|
|
test_empty();
|
|
#endif // TEST_HAS_NO_EXCEPTIONS
|
|
|
|
return 0;
|
|
}
|