184 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// constexpr auto synth-three-way = ...;12// via std::tuple<T>(t) <=> std::tuple<U>(u), which exposes its behavior most directly13 14#include "test_macros.h"15 16TEST_CLANG_DIAGNOSTIC_IGNORED("-Wsign-compare")17TEST_GCC_DIAGNOSTIC_IGNORED("-Wsign-compare")18TEST_MSVC_DIAGNOSTIC_IGNORED(4242 4244) // Various truncation warnings19 20#include <cassert>21#include <compare>22#include <limits> // quiet_NaN23#include <tuple>24#include <type_traits>25#include <utility> // declval26 27template <typename T, typename U = T>28concept can_synth_three_way = requires(T t, U u) { std::tuple<T>(t) <=> std::tuple<U>(u); };29 30template <typename T, typename U>31constexpr auto synth_three_way(const T& t, const U& u) {32 return std::tuple<T>(t) <=> std::tuple<U>(u);33}34 35template <typename T, typename U>36using synth_three_way_result = decltype(std::declval<std::tuple<T>>() <=> std::declval<std::tuple<U>>());37 38// A custom three-way result type39struct CustomEquality {40 friend constexpr bool operator==(const CustomEquality&, int) noexcept { return true; }41 friend constexpr bool operator<(const CustomEquality&, int) noexcept { return false; }42 friend constexpr bool operator<(int, const CustomEquality&) noexcept { return false; }43};44 45constexpr bool test() {46 {47 assert(synth_three_way(1, 1) == std::strong_ordering::equal);48 assert(synth_three_way(2, 1) == std::strong_ordering::greater);49 assert(synth_three_way(1, 2) == std::strong_ordering::less);50 ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<int, int>);51 ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<short, long long int>);52 }53 {54 constexpr double nan = std::numeric_limits<double>::quiet_NaN();55 assert(synth_three_way(1.0, 1.0) == std::partial_ordering::equivalent);56 assert(synth_three_way(2.0, 1.0) == std::partial_ordering::greater);57 assert(synth_three_way(1.0, 2.0) == std::partial_ordering::less);58 assert(synth_three_way(nan, nan) == std::partial_ordering::unordered);59 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, double>);60 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, float>);61 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<double, int>);62 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<float, short>);63 }64 {65 struct StrongSpaceship {66 int value;67 constexpr bool operator==(const StrongSpaceship&) const = default;68 constexpr std::strong_ordering operator<=>(const StrongSpaceship& other) const { return value <=> other.value; }69 };70 assert(synth_three_way(StrongSpaceship{1}, StrongSpaceship{1}) == std::strong_ordering::equal);71 assert(synth_three_way(StrongSpaceship{2}, StrongSpaceship{1}) == std::strong_ordering::greater);72 assert(synth_three_way(StrongSpaceship{1}, StrongSpaceship{2}) == std::strong_ordering::less);73 ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<StrongSpaceship, StrongSpaceship>);74 }75 {76 struct WeakSpaceship {77 int value;78 constexpr bool operator==(const WeakSpaceship&) const = default;79 constexpr std::weak_ordering operator<=>(const WeakSpaceship& other) const {80 return value <=> other.value;81 }82 };83 assert(synth_three_way(WeakSpaceship{1}, WeakSpaceship{1}) == std::weak_ordering::equivalent);84 assert(synth_three_way(WeakSpaceship{2}, WeakSpaceship{1}) == std::weak_ordering::greater);85 assert(synth_three_way(WeakSpaceship{1}, WeakSpaceship{2}) == std::weak_ordering::less);86 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<WeakSpaceship, WeakSpaceship>);87 }88 {89 struct PartialSpaceship {90 double value;91 constexpr bool operator==(const PartialSpaceship&) const = default;92 constexpr std::partial_ordering operator<=>(const PartialSpaceship& other) const {93 return value <=> other.value;94 }95 };96 constexpr double nan = std::numeric_limits<double>::quiet_NaN();97 assert(synth_three_way(PartialSpaceship{1.0}, PartialSpaceship{1.0}) == std::partial_ordering::equivalent);98 assert(synth_three_way(PartialSpaceship{2.0}, PartialSpaceship{1.0}) == std::partial_ordering::greater);99 assert(synth_three_way(PartialSpaceship{1.0}, PartialSpaceship{2.0}) == std::partial_ordering::less);100 assert(synth_three_way(PartialSpaceship{nan}, PartialSpaceship{nan}) == std::partial_ordering::unordered);101 ASSERT_SAME_TYPE(std::partial_ordering, synth_three_way_result<PartialSpaceship, PartialSpaceship>);102 }103 {104 struct NoSpaceship {105 int value;106 constexpr bool operator==(const NoSpaceship&) const = default;107 constexpr bool operator<(const NoSpaceship& other) const { return value < other.value; }108 };109 assert(synth_three_way(NoSpaceship{1}, NoSpaceship{1}) == std::weak_ordering::equivalent);110 assert(synth_three_way(NoSpaceship{2}, NoSpaceship{1}) == std::weak_ordering::greater);111 assert(synth_three_way(NoSpaceship{1}, NoSpaceship{2}) == std::weak_ordering::less);112 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<NoSpaceship, NoSpaceship>);113 }114 {115 // Types with operator<=> but no operator== are not three_way_comparable and will fall back to operator< and116 // compare as weakly ordered.117 struct SpaceshipNoEquals {118 constexpr std::strong_ordering operator<=>(const SpaceshipNoEquals&) const {119 return std::strong_ordering::equivalent;120 }121 };122 assert(synth_three_way(SpaceshipNoEquals{}, SpaceshipNoEquals{}) == std::weak_ordering::equivalent);123 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<SpaceshipNoEquals, SpaceshipNoEquals>);124 }125 {126 // Custom three-way-comparison result types cannot satisfy standard concepts (and therefore synth-three-way)127 // because they are not understood by std::common_comparison_category, but they can still be used in128 // the same way as standard orderings to do comparisons, and thus can be used by synth-three-way to yield a129 // weakly-ordered result.130 struct CustomSpaceship {131 constexpr CustomEquality operator<=>(const CustomSpaceship&) const { return CustomEquality(); }132 };133 assert((CustomSpaceship{} <=> CustomSpaceship{}) == 0);134 assert(!(CustomSpaceship{} < CustomSpaceship{}));135 assert(synth_three_way(CustomSpaceship{}, CustomSpaceship{}) == std::weak_ordering::equivalent);136 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<CustomSpaceship, CustomSpaceship>);137 }138 // SFINAE tests demonstrating synth-three-way needs three_way_comparable or operator<.139 {140 struct NoRelative {141 constexpr bool operator==(const NoRelative&) const;142 };143 static_assert(!can_synth_three_way<NoRelative>);144 }145 {146 struct NoLessThan {147 constexpr bool operator==(const NoLessThan&) const;148 constexpr bool operator>(const NoLessThan&) const;149 constexpr bool operator>=(const NoLessThan&) const;150 constexpr bool operator<=(const NoLessThan&) const;151 };152 static_assert(!can_synth_three_way<NoLessThan>);153 }154 {155 assert(synth_three_way(1, 1U) == std::weak_ordering::equivalent);156 assert(synth_three_way(-1, 0U) == std::weak_ordering::greater);157 // Even with the warning suppressed (-Wno-sign-compare) there should still be no <=> operator158 // between signed and unsigned types, so we should end up with a synthesized weak ordering.159 ASSERT_SAME_TYPE(std::weak_ordering, synth_three_way_result<int, unsigned int>);160 // When an unsigned type can be narrowed to a larger signed type, <=> should be defined and we161 // should get a strong ordering. (This probably does not raise a warning due to safe narrowing.)162 assert(synth_three_way(static_cast<long long int>(-1), static_cast<unsigned char>(0)) == std::strong_ordering::less);163 assert(synth_three_way(static_cast<long long int>(-1), static_cast<unsigned char>(0)) == std::strong_ordering::less);164 ASSERT_SAME_TYPE(std::strong_ordering, synth_three_way_result<long long int, unsigned char>);165 }166#ifdef TEST_COMPILER_GCC167 // GCC cannot evaluate NaN @ non-NaN constexpr, so test that runtime-only.168 if (!std::is_constant_evaluated())169#endif170 {171 constexpr double nan = std::numeric_limits<double>::quiet_NaN();172 assert(synth_three_way(nan, 1.0) == std::partial_ordering::unordered);173 }174 175 return true;176}177 178int main(int, char**) {179 test();180 static_assert(test());181 182 return 0;183}184