diff --git a/libcxx/docs/Cxx2aStatusPaperStatus.csv b/libcxx/docs/Cxx2aStatusPaperStatus.csv --- a/libcxx/docs/Cxx2aStatusPaperStatus.csv +++ b/libcxx/docs/Cxx2aStatusPaperStatus.csv @@ -164,7 +164,7 @@ "`P1961 `__","LWG","Harmonizing the definitions of total order for pointers","Belfast","* *","" "`P1965 `__","LWG","Blanket Wording for Specifying ""Hidden Friends""","Belfast","* *","" "","","","","","" -"`P0586 `__","LWG","Safe integral comparisons","Prague","* *","" +"`P0586 `__","LWG","Safe integral comparisons","Prague","|Complete|","12.0" "`P0593 `__","CWG","Implicit creation of objects for low-level object manipulation","Prague","* *","" "`P1115 `__","LWG","Improving the Return Value of Erase-Like Algorithms II: Free erase/erase if","Prague","|Complete|","11.0" "`P1243 `__","LWG","Rangify New Algorithms","Prague","* *","" diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -238,7 +238,7 @@ ------------------------------------------------- ----------------- ``__cpp_lib_int_pow2`` ``202002L`` ------------------------------------------------- ----------------- - ``__cpp_lib_integer_comparison_functions`` *unimplemented* + ``__cpp_lib_integer_comparison_functions`` ``202002L`` ------------------------------------------------- ----------------- ``__cpp_lib_interpolate`` ``201902L`` ------------------------------------------------- ----------------- diff --git a/libcxx/include/utility b/libcxx/include/utility --- a/libcxx/include/utility +++ b/libcxx/include/utility @@ -58,6 +58,14 @@ template typename add_rvalue_reference::type declval() noexcept; +template constexpr bool cmp_equal(T t, U u) noexcept; // C++20 +template constexpr bool cmp_not_equal(T t, U u) noexcept; // C++20 +template constexpr bool cmp_less(T t, U u) noexcept; // C++20 +template constexpr bool cmp_greater(T t, U u) noexcept; // C++20 +template constexpr bool cmp_less_equal(T t, U u) noexcept; // C++20 +template constexpr bool cmp_greater_equal(T t, U u) noexcept; // C++20 +template constexpr bool in_range(T t) noexcept; // C++20 + template struct pair { @@ -202,6 +210,7 @@ #include #include #include +#include #include #include <__debug> @@ -209,6 +218,9 @@ #pragma GCC system_header #endif +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + _LIBCPP_BEGIN_NAMESPACE_STD namespace rel_ops @@ -276,6 +288,85 @@ template void as_const(const _Tp&&) = delete; #endif +#if _LIBCPP_STD_VER > 17 +#if defined(__cpp_concepts) && __cpp_concepts >= 201811L +template +struct __is_any_of; + +template +struct __is_any_of<_Ty1, _Ty2> { +constexpr bool static value = is_same_v<_Ty1, _Ty2>; +}; + +template +struct __is_any_of<_Ty, _Ty1, _Tyn...> { +constexpr bool static value = is_same_v<_Ty, _Ty1> || __is_any_of<_Ty, _Tyn...>::value; +}; + +template +concept __is_safe_integral_cmp = is_integral_v<_Ty> && + !__is_any_of, bool, char, char8_t, char16_t, char32_t, wchar_t, byte>::value; + +template +requires(__is_safe_integral_cmp<_T1> && __is_safe_integral_cmp<_T2>) +constexpr bool cmp_equal(const _T1 t1, const _T2 t2) noexcept +{ + using T1U = make_unsigned_t<_T1>; + using T2U = make_unsigned_t<_T2>; + if constexpr (is_signed_v<_T1> == is_signed_v<_T2>) + return t1 == t2; + else if constexpr (is_signed_v<_T1>) + return t1 < 0 ? false : T1U(t1) == t2; + else + return t2 < 0 ? false : t1 == T2U(t2); +} + +template +constexpr bool cmp_not_equal(const _T1 t1, const _T2 t2) noexcept +{ + return !cmp_equal(t1, t2); +} + +template +requires(__is_safe_integral_cmp<_T1> && __is_safe_integral_cmp<_T2>) +constexpr bool cmp_less(const _T1 t1, const _T2 t2) noexcept +{ + using T1U = make_unsigned_t<_T1>; + using T2U = make_unsigned_t<_T2>; + if constexpr (is_signed_v<_T1> == is_signed_v<_T2>) + return t1 < t2; + else if constexpr (is_signed_v<_T1>) + return t1 < 0 ? true : T1U(t1) < t2; + else + return t2 < 0 ? false : t1 < T2U(t2); +} + +template +constexpr bool cmp_greater(const _T1 t1, const _T2 t2) noexcept +{ + return cmp_less(t2, t1); +} + +template +constexpr bool cmp_less_equal(const _T1 t1, const _T2 t2) noexcept +{ + return !cmp_greater(t1, t2); +} + +template +constexpr bool cmp_greater_equal(const _T1 t1, const _T2 t2) noexcept +{ + return !cmp_less(t1, t2); +} + +template +constexpr bool in_range(const _Ty t) noexcept +{ + return cmp_less_equal(t, numeric_limits<_Rt>::max()) && cmp_greater_equal(t, numeric_limits<_Rt>::min()); +} +#endif +#endif + struct _LIBCPP_TEMPLATE_VIS piecewise_construct_t { explicit piecewise_construct_t() = default; }; #if defined(_LIBCPP_CXX03_LANG) || defined(_LIBCPP_BUILDING_LIBRARY) extern _LIBCPP_EXPORTED_FROM_ABI const piecewise_construct_t piecewise_construct;// = piecewise_construct_t(); @@ -1626,4 +1717,6 @@ _LIBCPP_END_NAMESPACE_STD +_LIBCPP_POP_MACROS + #endif // _LIBCPP_UTILITY diff --git a/libcxx/include/version b/libcxx/include/version --- a/libcxx/include/version +++ b/libcxx/include/version @@ -315,7 +315,9 @@ // # define __cpp_lib_execution 201902L # define __cpp_lib_generic_unordered_lookup 201811L # define __cpp_lib_int_pow2 202002L -// # define __cpp_lib_integer_comparison_functions 202002L +# if defined(__cpp_concepts) && __cpp_concepts >= 201811L +# define __cpp_lib_integer_comparison_functions 202002L +# endif # define __cpp_lib_interpolate 201902L # if !defined(_LIBCPP_HAS_NO_BUILTIN_IS_CONSTANT_EVALUATED) # define __cpp_lib_is_constant_evaluated 201811L diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/utility.version.pass.cpp @@ -171,16 +171,16 @@ # error "__cpp_lib_exchange_function should have the value 201304L in c++20" # endif -# if !defined(_LIBCPP_VERSION) +# if defined(__cpp_concepts) && __cpp_concepts >= 201811L # ifndef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should be defined in c++20" # endif # if __cpp_lib_integer_comparison_functions != 202002L # error "__cpp_lib_integer_comparison_functions should have the value 202002L in c++20" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_integer_comparison_functions -# error "__cpp_lib_integer_comparison_functions should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_integer_comparison_functions should not be defined when defined(__cpp_concepts) && __cpp_concepts >= 201811L is not defined!" # endif # endif @@ -234,16 +234,16 @@ # error "__cpp_lib_exchange_function should have the value 201304L in c++2b" # endif -# if !defined(_LIBCPP_VERSION) +# if defined(__cpp_concepts) && __cpp_concepts >= 201811L # ifndef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should be defined in c++2b" # endif # if __cpp_lib_integer_comparison_functions != 202002L # error "__cpp_lib_integer_comparison_functions should have the value 202002L in c++2b" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_integer_comparison_functions -# error "__cpp_lib_integer_comparison_functions should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_integer_comparison_functions should not be defined when defined(__cpp_concepts) && __cpp_concepts >= 201811L is not defined!" # endif # endif diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp --- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp +++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp @@ -2650,16 +2650,16 @@ # error "__cpp_lib_int_pow2 should have the value 202002L in c++20" # endif -# if !defined(_LIBCPP_VERSION) +# if defined(__cpp_concepts) && __cpp_concepts >= 201811L # ifndef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should be defined in c++20" # endif # if __cpp_lib_integer_comparison_functions != 202002L # error "__cpp_lib_integer_comparison_functions should have the value 202002L in c++20" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_integer_comparison_functions -# error "__cpp_lib_integer_comparison_functions should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_integer_comparison_functions should not be defined when defined(__cpp_concepts) && __cpp_concepts >= 201811L is not defined!" # endif # endif @@ -3862,16 +3862,16 @@ # error "__cpp_lib_int_pow2 should have the value 202002L in c++2b" # endif -# if !defined(_LIBCPP_VERSION) +# if defined(__cpp_concepts) && __cpp_concepts >= 201811L # ifndef __cpp_lib_integer_comparison_functions # error "__cpp_lib_integer_comparison_functions should be defined in c++2b" # endif # if __cpp_lib_integer_comparison_functions != 202002L # error "__cpp_lib_integer_comparison_functions should have the value 202002L in c++2b" # endif -# else // _LIBCPP_VERSION +# else # ifdef __cpp_lib_integer_comparison_functions -# error "__cpp_lib_integer_comparison_functions should not be defined because it is unimplemented in libc++!" +# error "__cpp_lib_integer_comparison_functions should not be defined when defined(__cpp_concepts) && __cpp_concepts >= 201811L is not defined!" # endif # endif diff --git a/libcxx/test/std/utilities/utility/safe_int_cmp/safe_int_cmp.fail.cpp b/libcxx/test/std/utilities/utility/safe_int_cmp/safe_int_cmp.fail.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/safe_int_cmp/safe_int_cmp.fail.cpp @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// + +#include +#include + +int main() { + // cmp_equal + static_assert(std::cmp_equal(true, true)); // expected-error{{no matching function for call to 'cmp_equal'}} + static_assert(std::cmp_equal('a', 'a')); // expected-error{{no matching function for call to 'cmp_equal'}} + static_assert(std::cmp_equal(u8'a', u8'a')); // expected-error{{no matching function for call to 'cmp_equal'}} + static_assert(std::cmp_equal(u'a', u'a')); // expected-error{{no matching function for call to 'cmp_equal'}} + static_assert(std::cmp_equal(U'a', U'a')); // expected-error{{no matching function for call to 'cmp_equal'}} + static_assert(std::cmp_equal(L'a', L'a')); // expected-error{{no matching function for call to 'cmp_equal'}} + static_assert(std::cmp_equal(std::byte{1}, std::byte{1})); // expected-error{{no matching function for call to 'cmp_equal'}} + static_assert(std::cmp_equal(1.0f, 1.0f)); // expected-error{{no matching function for call to 'cmp_equal'}} + // cmp_less + static_assert(std::cmp_less(true, false)); // expected-error{{no matching function for call to 'cmp_less'}} + static_assert(std::cmp_less('a', 'b')); // expected-error{{no matching function for call to 'cmp_less'}} + static_assert(std::cmp_less(u8'a', u8'b')); // expected-error{{no matching function for call to 'cmp_less'}} + static_assert(std::cmp_less(u'a', u'b')); // expected-error{{no matching function for call to 'cmp_less'}} + static_assert(std::cmp_less(U'a', U'b')); // expected-error{{no matching function for call to 'cmp_less'}} + static_assert(std::cmp_less(L'a', L'b')); // expected-error{{no matching function for call to 'cmp_less'}} + static_assert(std::cmp_less(std::byte{1}, std::byte{2})); // expected-error{{no matching function for call to 'cmp_less'}} + static_assert(std::cmp_less(1.0f, 2.0f)); // expected-error{{no matching function for call to 'cmp_less'}} +return 0; +} diff --git a/libcxx/test/std/utilities/utility/safe_int_cmp/safe_int_cmp.pass.cpp b/libcxx/test/std/utilities/utility/safe_int_cmp/safe_int_cmp.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/utility/safe_int_cmp/safe_int_cmp.pass.cpp @@ -0,0 +1,84 @@ +//===----------------------------------------------------------------------===// +// +// 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 + +// +// + +#include +#include + +using namespace std; + +int main() { + // Compile tests for in_range + static_assert(in_range(1), "in range"); + static_assert(in_range(1u), "in range"); + static_assert(in_range(1ul), "in range"); + static_assert(in_range(-1l), "in range"); + + static_assert(!in_range(std::numeric_limits::max()), "in range"); + static_assert(in_range(std::numeric_limits::max()), "in range"); + static_assert(!in_range(-1), "in range, negative value, unsigned range"); + static_assert(in_range(-1), "in range, negative value, unsigned range"); + static_assert(in_range(std::numeric_limits::min()), "in range"); + static_assert(in_range(std::numeric_limits::min()), "in range"); + static_assert(!in_range(std::numeric_limits::min()), "in range"); + static_assert(!in_range(std::numeric_limits::min()), "in range"); + + // Compile tests for cmp_equal + static_assert(cmp_equal(1, 1), "comparison same signed type, same value"); + static_assert(cmp_equal(1u, 1u), "comparison same unsigned type, same value (2)"); + static_assert(cmp_equal(1ul, 1u), "comparison unsigned types, same value"); + static_assert(cmp_equal(1u, 1ul), "comparison unsigned types, same value (2)"); + static_assert(cmp_equal(1l, 1), "comparison signed types, same value"); + static_assert(cmp_equal(1, 1l), "comparison signed types, same value (2)"); + static_assert(cmp_equal(1ul, 1), "comparison signed/unsigned types, same value"); + static_assert(cmp_equal(1, 1ul), "comparison signed/unsigned types, same value (2)"); + + static_assert(!cmp_equal(1, 2), "comparison same signed type, different values"); + static_assert(!cmp_equal(1, -1), "comparison same signed type, different values (2)"); + static_assert(!cmp_equal(1u, 2u), "comparison same unsigned type, different values"); + static_assert(!cmp_equal(1ul, 2u), "comparison unsigned types, different values"); + static_assert(!cmp_equal(2u, 1ul), "comparison unsigned types, different values (2)"); + static_assert(!cmp_equal(1l, 2), "comparison signed types, different values"); + static_assert(!cmp_equal(2, 1l), "comparison signed types, different values (2)"); + static_assert(!cmp_equal(2ul, 1), "comparison signed/unsigned types, different values"); + static_assert(!cmp_equal(1, 2ul), "comparison signed/unsigned types, different values (2)"); + static_assert(!cmp_equal(std::numeric_limits::max(), std::numeric_limits::max()), + "comparison unsigned/signed type"); + static_assert(!cmp_equal(std::numeric_limits::max(), -1), + "comparison unsigned/signed type"); + + // Compile tests for cmp_less + static_assert(!cmp_less(1, 1), "comparison same signed type, same value"); + static_assert(!cmp_less(1u, 1u), "comparison same unsigned type, same value (2)"); + static_assert(!cmp_less(1ul, 1u), "comparison unsigned types, same value"); + static_assert(!cmp_less(1u, 1ul), "comparison unsigned types, same value (2)"); + static_assert(!cmp_less(1l, 1), "comparison signed types, same value"); + static_assert(!cmp_less(1, 1l), "comparison signed types, same value (2)"); + static_assert(!cmp_less(1ul, 1), "comparison signed/unsigned types, same value"); + static_assert(!cmp_less(1, 1ul), "comparison signed/unsigned types, same value (2)"); + + static_assert(cmp_less(1, 2), "comparison same signed type, different values"); + static_assert(!cmp_less(1, -1), "comparison same signed type, different values (2)"); + static_assert(cmp_less(1u, 2u), "comparison same unsigned type, different values"); + static_assert(cmp_less(1ul, 2u) ,"comparison unsigned types, different values"); + static_assert(!cmp_less(2u, 1ul),"comparison unsigned types, different values (2)"); + static_assert(cmp_less(1l, 2), "comparison signed types, different values"); + static_assert(!cmp_less(2, 1l), "comparison signed types, different values (2)"); + static_assert(cmp_less(1ul, 2), "comparison signed/unsigned types, different values"); + static_assert(!cmp_less(2, 1ul), "comparison signed/unsigned types, different values (2)"); + static_assert(!cmp_less(std::numeric_limits::max(), std::numeric_limits::max()), + "comparison unsigned/signed type"); + static_assert(!cmp_less(std::numeric_limits::max(), -1), + "comparison unsigned/signed type"); + return 0; +} diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py --- a/libcxx/utils/generate_feature_test_macro_components.py +++ b/libcxx/utils/generate_feature_test_macro_components.py @@ -309,7 +309,8 @@ "name": "__cpp_lib_integer_comparison_functions", "values": { "c++20": 202002 }, "headers": ["utility"], - "unimplemented": True, + "depends": "defined(__cpp_concepts) && __cpp_concepts >= 201811L", + "internal_depends": "defined(__cpp_concepts) && __cpp_concepts >= 201811L", }, { "name": "__cpp_lib_integer_sequence", "values": { "c++14": 201304 },