diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -99,6 +99,7 @@ __bsd_locale_fallbacks.h __compare/common_comparison_category.h __compare/ordering.h + __compare/three_way_comparable.h __concepts/arithmetic.h __concepts/assignable.h __concepts/boolean_testable.h diff --git a/libcxx/include/__compare/three_way_comparable.h b/libcxx/include/__compare/three_way_comparable.h new file mode 100644 --- /dev/null +++ b/libcxx/include/__compare/three_way_comparable.h @@ -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 +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___COMPARE_THREE_WAY_COMPARABLE_H +#define _LIBCPP___COMPARE_THREE_WAY_COMPARABLE_H + +#include <__compare/common_comparison_category.h> +#include <__compare/ordering.h> +#include <__concepts/common_reference_with.h> +#include <__concepts/equality_comparable.h> +#include <__concepts/same_as.h> +#include <__concepts/totally_ordered.h> +#include <__config> +#include + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +#pragma GCC system_header +#endif + +_LIBCPP_BEGIN_NAMESPACE_STD + +#if _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +template +concept __compares_as = + same_as, _Cat>; + +template +concept three_way_comparable = + __weakly_equality_comparable_with<_Tp, _Tp> && + __partially_ordered_with<_Tp, _Tp> && + requires(__make_const_lvalue_ref<_Tp> __a, __make_const_lvalue_ref<_Tp> __b) { + { __a <=> __b } -> __compares_as<_Cat>; + }; + +template +concept three_way_comparable_with = + three_way_comparable<_Tp, _Cat> && + three_way_comparable<_Up, _Cat> && + common_reference_with<__make_const_lvalue_ref<_Tp>, __make_const_lvalue_ref<_Up>> && + three_way_comparable, __make_const_lvalue_ref<_Up>>, _Cat> && + __weakly_equality_comparable_with<_Tp, _Up> && + __partially_ordered_with<_Tp, _Up> && + requires(__make_const_lvalue_ref<_Tp> __t, __make_const_lvalue_ref<_Up> __u) { + { __t <=> __u } -> __compares_as<_Cat>; + { __u <=> __t } -> __compares_as<_Cat>; + }; + +#endif // _LIBCPP_STD_VER > 17 && !defined(_LIBCPP_HAS_NO_SPACESHIP_OPERATOR) && !defined(_LIBCPP_HAS_NO_CONCEPTS) + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP___COMPARE_THREE_WAY_COMPARABLE_H diff --git a/libcxx/include/compare b/libcxx/include/compare --- a/libcxx/include/compare +++ b/libcxx/include/compare @@ -35,6 +35,12 @@ template using common_comparison_category_t = typename common_comparison_category::type; + // [cmp.concept], concept three_way_comparable + template + concept three_way_comparable = see below; + template + concept three_way_comparable_with = see below; + // [cmp.alg], comparison algorithms template constexpr strong_ordering strong_order(const T& a, const T& b); template constexpr weak_ordering weak_order(const T& a, const T& b); @@ -122,6 +128,7 @@ #include <__compare/common_comparison_category.h> #include <__compare/ordering.h> +#include <__compare/three_way_comparable.h> #include <__config> #include diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -362,6 +362,7 @@ module __compare { module common_comparison_category { private header "__compare/common_comparison_category.h" } module ordering { private header "__compare/ordering.h" } + module three_way_comparable { private header "__compare/three_way_comparable.h" } } } module complex { diff --git a/libcxx/test/libcxx/diagnostics/detail.headers/compare/three_way_comparable.module.verify.cpp b/libcxx/test/libcxx/diagnostics/detail.headers/compare/three_way_comparable.module.verify.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/diagnostics/detail.headers/compare/three_way_comparable.module.verify.cpp @@ -0,0 +1,16 @@ +// -*- 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 +// +//===----------------------------------------------------------------------===// + +// REQUIRES: modules-build + +// WARNING: This test was generated by 'generate_private_header_tests.py' +// and should not be edited manually. + +// expected-error@*:* {{use of private header from outside its module: '__compare/three_way_comparable.h'}} +#include <__compare/three_way_comparable.h> diff --git a/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable.compile.pass.cpp b/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable.compile.pass.cpp @@ -0,0 +1,226 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts + +// template +// concept three_way_comparable = // see below + +#include + +#include "compare_types.h" + +namespace fundamentals { +// with default ordering +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +#ifndef _LIBCPP_HAS_NO_CHAR8_T +static_assert(std::three_way_comparable); +#endif +#ifndef _LIBCPP_HAS_NO_UNICODE_CHARS +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +#endif +#ifndef _LIBCPP_HAS_NO_INT128 +static_assert(std::three_way_comparable<__int128_t const&>); +static_assert(std::three_way_comparable<__uint128_t const&>); +#endif +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); + +// with explicit ordering +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); + +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); + +struct S {}; +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +static_assert(!std::three_way_comparable); +} // namespace fundamentals + +namespace user_defined { + +struct S { + auto operator<=>(const S&) const = default; +}; + +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); +static_assert(std::three_way_comparable); + +struct SpaceshipNotDeclared { +}; + +static_assert(!std::three_way_comparable); + +struct SpaceshipDeleted { + auto operator<=>(const SpaceshipDeleted&) const = delete; +}; + +static_assert(!std::three_way_comparable); + +struct SpaceshipWithoutEqualityOperator { + auto operator<=>(const SpaceshipWithoutEqualityOperator&) const; +}; + +static_assert(!std::three_way_comparable); + +struct EqualityOperatorDeleted { + bool operator==(const EqualityOperatorDeleted&) const = delete; +}; + +static_assert(!std::three_way_comparable); + +struct EqualityOperatorOnly { + bool operator==(const EqualityOperatorOnly&) const = default; +}; + +static_assert(!std::three_way_comparable); + +struct SpaceshipDeclaredEqualityOperatorDeleted { + bool operator==(const SpaceshipDeclaredEqualityOperatorDeleted&) const = delete; + auto operator<=>(const SpaceshipDeclaredEqualityOperatorDeleted&) const = default; +}; + +static_assert(!std::three_way_comparable); + +struct AllInequalityOperators { + bool operator<(const AllInequalityOperators&) const; + bool operator<=(const AllInequalityOperators&) const; + bool operator>(const AllInequalityOperators&) const; + bool operator>=(const AllInequalityOperators&) const; + bool operator!=(const AllInequalityOperators&) const; +}; + +static_assert(!std::three_way_comparable); + +struct AllComparisonOperators { + bool operator<(const AllComparisonOperators&) const; + bool operator<=(const AllComparisonOperators&) const; + bool operator>(const AllComparisonOperators&) const; + bool operator>=(const AllComparisonOperators&) const; + bool operator!=(const AllComparisonOperators&) const; + bool operator==(const AllComparisonOperators&) const; +}; + +static_assert(!std::three_way_comparable); + +struct AllButOneInequalityOperators { + bool operator<(const AllButOneInequalityOperators&) const; + bool operator<=(const AllButOneInequalityOperators&) const; + bool operator>(const AllButOneInequalityOperators&) const; + bool operator!=(const AllButOneInequalityOperators&) const; +}; + +static_assert(!std::three_way_comparable); + +struct AllInequalityOperatorsOneDeleted { + bool operator<(const AllInequalityOperatorsOneDeleted&) const; + bool operator<=(const AllInequalityOperatorsOneDeleted&) const; + bool operator>(const AllInequalityOperatorsOneDeleted&) const; + bool operator>=(const AllInequalityOperatorsOneDeleted&) const = delete; + bool operator!=(const AllInequalityOperatorsOneDeleted&) const; +}; + +static_assert(!std::three_way_comparable); + +struct EqualityOperatorWrongReturnType { + int operator==(const EqualityOperatorWrongReturnType&); + auto operator<=>(const EqualityOperatorWrongReturnType&) const = default; +}; + +static_assert(!std::three_way_comparable); + +struct SpaceshipWrongReturnType { + bool operator==(const SpaceshipWrongReturnType&) const = default; + int operator<=>(const SpaceshipWrongReturnType&); +}; + +static_assert(!std::three_way_comparable); + +struct EqualityOperatorNonConstArgument { + bool operator==(EqualityOperatorNonConstArgument&); + auto operator<=>(const EqualityOperatorNonConstArgument&) const = default; +}; + +static_assert(!std::three_way_comparable); + +struct SpaceshipNonConstArgument { + bool operator==(const SpaceshipNonConstArgument&) const = default; + auto operator<=>(SpaceshipNonConstArgument&); +}; + +static_assert(!std::three_way_comparable); +} // namespace user_defined diff --git a/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable_with.compile.pass.cpp b/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable_with.compile.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/language.support/cmp/cmp.concept/three_way_comparable_with.compile.pass.cpp @@ -0,0 +1,227 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// UNSUPPORTED: libcpp-no-concepts + +// template +// concept three_way_comparable_with = // see below + +#include + +#include "compare_types.h" + +template +constexpr bool check_three_way_comparable_with() { + constexpr bool result = std::three_way_comparable_with; + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + if constexpr (!std::is_void_v) { + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + static_assert(std::three_way_comparable_with == result); + } + return result; +} + +namespace fundamentals { +static_assert(check_three_way_comparable_with()); +static_assert(check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(check_three_way_comparable_with()); +static_assert(check_three_way_comparable_with()); + +static_assert(check_three_way_comparable_with()); +static_assert(check_three_way_comparable_with()); +static_assert(check_three_way_comparable_with()); + +static_assert(check_three_way_comparable_with()); +static_assert(check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); + +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +struct S {}; +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +static_assert(!check_three_way_comparable_with()); +} // namespace fundamentals + +namespace user_defined { +struct S { + bool operator==(int) const; + std::strong_ordering operator<=>(int) const; + operator int() const; + + bool operator==(const S&) const = default; + auto operator<=>(const S&) const = default; +}; + +static_assert(check_three_way_comparable_with()); +static_assert(check_three_way_comparable_with()); +static_assert(check_three_way_comparable_with()); + +struct SpaceshipNotDeclared { +}; + +static_assert(!check_three_way_comparable_with()); + +struct SpaceshipDeleted { + auto operator<=>(const SpaceshipDeleted&) const = delete; +}; + +static_assert(!check_three_way_comparable_with()); + +struct SpaceshipWithoutEqualityOperator { + auto operator<=>(const SpaceshipWithoutEqualityOperator&) const; +}; + +static_assert(!check_three_way_comparable_with()); + +struct EqualityOperatorDeleted { + bool operator==(const EqualityOperatorDeleted&) const = delete; +}; + +static_assert(!check_three_way_comparable_with()); + +struct EqualityOperatorOnly { + bool operator==(const EqualityOperatorOnly&) const = default; +}; + +static_assert(!check_three_way_comparable_with()); + +struct SpaceshipDeclaredEqualityOperatorDeleted { + bool operator==(const SpaceshipDeclaredEqualityOperatorDeleted&) const = delete; + auto operator<=>(const SpaceshipDeclaredEqualityOperatorDeleted&) const = default; +}; + +static_assert(!check_three_way_comparable_with()); + +struct AllInequalityOperators { + bool operator<(const AllInequalityOperators&) const; + bool operator<=(const AllInequalityOperators&) const; + bool operator>(const AllInequalityOperators&) const; + bool operator>=(const AllInequalityOperators&) const; + bool operator!=(const AllInequalityOperators&) const; +}; + +static_assert(!check_three_way_comparable_with()); + +struct AllComparisonOperators { + bool operator<(const AllComparisonOperators&) const; + bool operator<=(const AllComparisonOperators&) const; + bool operator>(const AllComparisonOperators&) const; + bool operator>=(const AllComparisonOperators&) const; + bool operator!=(const AllComparisonOperators&) const; + bool operator==(const AllComparisonOperators&) const; +}; + +static_assert(!check_three_way_comparable_with()); + +struct AllButOneInequalityOperators { + bool operator<(const AllButOneInequalityOperators&) const; + bool operator<=(const AllButOneInequalityOperators&) const; + bool operator>(const AllButOneInequalityOperators&) const; + bool operator!=(const AllButOneInequalityOperators&) const; +}; + +static_assert(!check_three_way_comparable_with()); + +struct AllInequalityOperatorsOneDeleted { + bool operator<(const AllInequalityOperatorsOneDeleted&) const; + bool operator<=(const AllInequalityOperatorsOneDeleted&) const; + bool operator>(const AllInequalityOperatorsOneDeleted&) const; + bool operator>=(const AllInequalityOperatorsOneDeleted&) const = delete; + bool operator!=(const AllInequalityOperatorsOneDeleted&) const; +}; + +static_assert(!check_three_way_comparable_with()); + +struct EqualityOperatorWrongReturnType { + int operator==(const EqualityOperatorWrongReturnType&); + auto operator<=>(const EqualityOperatorWrongReturnType&) const = default; +}; + +static_assert(!check_three_way_comparable_with()); + +struct SpaceshipWrongReturnType { + bool operator==(const SpaceshipWrongReturnType&) const = default; + int operator<=>(const SpaceshipWrongReturnType&); +}; + +static_assert(!check_three_way_comparable_with()); + +struct EqualityOperatorNonConstArgument { + bool operator==(EqualityOperatorNonConstArgument&); + auto operator<=>(const EqualityOperatorNonConstArgument&) const = default; +}; + +static_assert(!check_three_way_comparable_with()); + +struct SpaceshipNonConstArgument { + bool operator==(const SpaceshipNonConstArgument&) const = default; + auto operator<=>(SpaceshipNonConstArgument&); +}; + +static_assert(!check_three_way_comparable_with()); +} // namespace user_defined