diff --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv --- a/libcxx/docs/Status/SpaceshipProjects.csv +++ b/libcxx/docs/Status/SpaceshipProjects.csv @@ -77,5 +77,5 @@ | `[fs.filesystem.syn] `_,| `filesystem::space_info `_,None,Adrian Vogelsgesang,|Complete| | `[fs.path.nonmember] `_,| `filesystem::path `_,None,Adrian Vogelsgesang,|Complete| | `[fs.dir.entry.obs] `_,| `filesystem::directory_entry `_,None,Adrian Vogelsgesang,|Complete| -| `[re.submatch.op] `_,| `sub_match `_,None,Mark de Wever,|In Progress| +| `[re.submatch.op] `_,| `sub_match `_,None,Mark de Wever,|Complete| | `[thread.thread.id] `_,| `thread::id `_,None,Adrian Vogelsgesang,|Complete| diff --git a/libcxx/include/regex b/libcxx/include/regex --- a/libcxx/include/regex +++ b/libcxx/include/regex @@ -13,6 +13,7 @@ /* regex synopsis +#include #include namespace std @@ -239,50 +240,54 @@ operator==(const sub_match& lhs, const sub_match& rhs); template + auto + operator<=>(const sub_match& lhs, const sub_match& rhs); // Since C++20 + + template // Removed in C++20 bool operator!=(const sub_match& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator<(const sub_match& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator<=(const sub_match& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator>=(const sub_match& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator>(const sub_match& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator==(const basic_string::value_type, ST, SA>& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator!=(const basic_string::value_type, ST, SA>& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator<(const basic_string::value_type, ST, SA>& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator>(const basic_string::value_type, ST, SA>& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator>=(const basic_string::value_type, ST, SA>& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator<=(const basic_string::value_type, ST, SA>& lhs, const sub_match& rhs); @@ -292,56 +297,62 @@ operator==(const sub_match& lhs, const basic_string::value_type, ST, SA>& rhs); -template +template // Since C++20 + auto + operator<=>(const sub_match& lhs, + const basic_string::value_type, ST, SA>& rhs); + +template // Removed in C++20 bool operator!=(const sub_match& lhs, const basic_string::value_type, ST, SA>& rhs); -template +template // Removed in C++20 bool operator<(const sub_match& lhs, const basic_string::value_type, ST, SA>& rhs); -template - bool operator>(const sub_match& lhs, +template // Removed in C++20 + bool + operator>(const sub_match& lhs, const basic_string::value_type, ST, SA>& rhs); -template +template // Removed in C++20 bool operator>=(const sub_match& lhs, const basic_string::value_type, ST, SA>& rhs); -template +template // Removed in C++20 bool operator<=(const sub_match& lhs, const basic_string::value_type, ST, SA>& rhs); -template +template // Removed in C++20 bool operator==(typename iterator_traits::value_type const* lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator!=(typename iterator_traits::value_type const* lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator<(typename iterator_traits::value_type const* lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator>(typename iterator_traits::value_type const* lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator>=(typename iterator_traits::value_type const* lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator<=(typename iterator_traits::value_type const* lhs, const sub_match& rhs); @@ -351,57 +362,62 @@ operator==(const sub_match& lhs, typename iterator_traits::value_type const* rhs); -template +template // Since C++20 + auto + operator<=>(const sub_match& lhs, + typename iterator_traits::value_type const* rhs); + +template // Removed in C++20 bool operator!=(const sub_match& lhs, typename iterator_traits::value_type const* rhs); -template +template // Removed in C++20 bool operator<(const sub_match& lhs, typename iterator_traits::value_type const* rhs); -template +template // Removed in C++20 bool operator>(const sub_match& lhs, typename iterator_traits::value_type const* rhs); -template +template // Removed in C++20 bool operator>=(const sub_match& lhs, typename iterator_traits::value_type const* rhs); -template +template // Removed in C++20 bool operator<=(const sub_match& lhs, typename iterator_traits::value_type const* rhs); -template +template // Removed in C++20 bool operator==(typename iterator_traits::value_type const& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator!=(typename iterator_traits::value_type const& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator<(typename iterator_traits::value_type const& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator>(typename iterator_traits::value_type const& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator>=(typename iterator_traits::value_type const& lhs, const sub_match& rhs); -template +template // Removed in C++20 bool operator<=(typename iterator_traits::value_type const& lhs, const sub_match& rhs); @@ -411,27 +427,32 @@ operator==(const sub_match& lhs, typename iterator_traits::value_type const& rhs); -template +template // Since C++20 + auto + operator<=>(const sub_match& lhs, + typename iterator_traits::value_type const& rhs); + +template // Removed in C++20 bool operator!=(const sub_match& lhs, typename iterator_traits::value_type const& rhs); -template +template // Removed in C++20 bool operator<(const sub_match& lhs, typename iterator_traits::value_type const& rhs); -template +template // Removed in C++20 bool operator>(const sub_match& lhs, typename iterator_traits::value_type const& rhs); -template +template // Removed in C++20 bool operator>=(const sub_match& lhs, typename iterator_traits::value_type const& rhs); -template +template // Removed in C++20 bool operator<=(const sub_match& lhs, typename iterator_traits::value_type const& rhs); @@ -5032,6 +5053,15 @@ return __x.compare(__y) == 0; } +#if _LIBCPP_STD_VER >= 20 +template +using __sub_match_cat = compare_three_way_result_t::value_type>>; + +template +_LIBCPP_HIDE_FROM_ABI auto operator<=>(const sub_match<_BiIter>& __x, const sub_match<_BiIter>& __y) { + return static_cast<__sub_match_cat<_BiIter>>(__x.compare(__y) <=> 0); +} +#else // _LIBCPP_STD_VER >= 20 template inline _LIBCPP_INLINE_VISIBILITY bool @@ -5124,6 +5154,7 @@ { return !(__y < __x); } +#endif // _LIBCPP_STD_VER >= 20 template inline _LIBCPP_INLINE_VISIBILITY @@ -5134,6 +5165,14 @@ return __x.compare(typename sub_match<_BiIter>::string_type(__y.data(), __y.size())) == 0; } +#if _LIBCPP_STD_VER >= 20 +template +_LIBCPP_HIDE_FROM_ABI auto operator<=>( + const sub_match<_BiIter>& __x, const basic_string::value_type, _ST, _SA>& __y) { + return static_cast<__sub_match_cat<_BiIter>>( + __x.compare(typename sub_match<_BiIter>::string_type(__y.data(), __y.size())) <=> 0); +} +#else // _LIBCPP_STD_VER >= 20 template inline _LIBCPP_INLINE_VISIBILITY bool @@ -5231,6 +5270,7 @@ { return !(__y < __x); } +#endif // _LIBCPP_STD_VER >= 20 template inline _LIBCPP_INLINE_VISIBILITY @@ -5241,6 +5281,13 @@ return __x.compare(__y) == 0; } +#if _LIBCPP_STD_VER >= 20 +template +_LIBCPP_HIDE_FROM_ABI auto +operator<=>(const sub_match<_BiIter>& __x, typename iterator_traits<_BiIter>::value_type const* __y) { + return static_cast<__sub_match_cat<_BiIter>>(__x.compare(__y) <=> 0); +} +#else // _LIBCPP_STD_VER >= 20 template inline _LIBCPP_INLINE_VISIBILITY bool @@ -5341,6 +5388,7 @@ { return !(__y < __x); } +#endif // _LIBCPP_STD_VER >= 20 template inline _LIBCPP_INLINE_VISIBILITY @@ -5352,6 +5400,14 @@ return __x.compare(string_type(1, __y)) == 0; } +#if _LIBCPP_STD_VER >= 20 +template +_LIBCPP_HIDE_FROM_ABI auto +operator<=>(const sub_match<_BiIter>& __x, typename iterator_traits<_BiIter>::value_type const& __y) { + using string_type = basic_string::value_type>; + return static_cast<__sub_match_cat<_BiIter>>(__x.compare(string_type(1, __y)) <=> 0); +} +#else // _LIBCPP_STD_VER >= 20 template inline _LIBCPP_INLINE_VISIBILITY bool @@ -5397,6 +5453,7 @@ { return !(__y < __x); } +#endif // _LIBCPP_STD_VER >= 20 template inline _LIBCPP_INLINE_VISIBILITY diff --git a/libcxx/test/std/re/re.submatch/re.submatch.op/compare.pass.cpp b/libcxx/test/std/re/re.submatch/re.submatch.op/compare.pass.cpp --- a/libcxx/test/std/re/re.submatch/re.submatch.op/compare.pass.cpp +++ b/libcxx/test/std/re/re.submatch/re.submatch.op/compare.pass.cpp @@ -10,6 +10,9 @@ // template class sub_match; +// Note in C++20 several of these operators have been removed and implicitly +// generated by the compiler using operator== and operator<=>. + // template // bool // operator==(const sub_match& lhs, const sub_match& rhs); @@ -212,16 +215,50 @@ // operator<=(const sub_match& lhs, // typename iterator_traits::value_type const& rhs); +// Added in C++20 +// template +// auto +// operator<=>(const sub_match& lhs, const sub_match& rhs); +// template +// auto +// operator<=>(const sub_match& lhs, +// const basic_string::value_type, ST, SA>& rhs); +// +// template +// auto +// operator<=>(const sub_match& lhs, +// typename iterator_traits::value_type const* rhs); +// +// template +// auto +// operator<=>(const sub_match& lhs, +// typename iterator_traits::value_type const& rhs); + #include +#include #include + +#include "constexpr_char_traits.h" +#include "make_string.h" +#include "test_comparisons.h" #include "test_macros.h" + +#define SV(S) MAKE_STRING_VIEW(CharT, S) + template void test(const std::basic_string& x, const std::basic_string& y, bool doCStrTests = true) { typedef std::basic_string string; typedef std::sub_match sub_match; +#if TEST_STD_VER > 17 + AssertOrderReturn(); + AssertOrderReturn(); +#else + AssertComparisonsReturnBool(); + AssertComparisonsReturnBool(); +#endif sub_match sm1; sm1.first = x.begin(); sm1.second = x.end(); @@ -230,62 +267,136 @@ sm2.first = y.begin(); sm2.second = y.end(); sm2.matched = true; - assert((sm1 == sm2) == (x == y)); - assert((sm1 != sm2) == (x != y)); - assert((sm1 < sm2) == (x < y)); - assert((sm1 > sm2) == (x > y)); - assert((sm1 <= sm2) == (x <= y)); - assert((sm1 >= sm2) == (x >= y)); - assert((x == sm2) == (x == y)); - assert((x != sm2) == (x != y)); - assert((x < sm2) == (x < y)); - assert((x > sm2) == (x > y)); - assert((x <= sm2) == (x <= y)); - assert((x >= sm2) == (x >= y)); - assert((sm1 == y) == (x == y)); - assert((sm1 != y) == (x != y)); - assert((sm1 < y) == (x < y)); - assert((sm1 > y) == (x > y)); - assert((sm1 <= y) == (x <= y)); - assert((sm1 >= y) == (x >= y)); + + assert(testComparisons(sm1, sm2, x == y, x < y)); + assert(testComparisons(x, sm2, x == y, x < y)); + assert(testComparisons(sm1, y, x == y, x < y)); +#if TEST_STD_VER > 17 + assert(testOrder(sm1, sm2, x <=> y)); + assert(testOrder(x, sm2, x <=> y)); + assert(testOrder(sm1, y, x <=> y)); +#endif + if (doCStrTests) { - assert((x.c_str() == sm2) == (x == y)); - assert((x.c_str() != sm2) == (x != y)); - assert((x.c_str() < sm2) == (x < y)); - assert((x.c_str() > sm2) == (x > y)); - assert((x.c_str() <= sm2) == (x <= y)); - assert((x.c_str() >= sm2) == (x >= y)); - assert((sm1 == y.c_str()) == (x == y)); - assert((sm1 != y.c_str()) == (x != y)); - assert((sm1 < y.c_str()) == (x < y)); - assert((sm1 > y.c_str()) == (x > y)); - assert((sm1 <= y.c_str()) == (x <= y)); - assert((sm1 >= y.c_str()) == (x >= y)); + assert(testComparisons(x.c_str(), sm2, x == y, x < y)); + assert(testComparisons(sm1, y.c_str(), x == y, x < y)); +#if TEST_STD_VER > 17 + assert(testOrder(x.c_str(), sm2, x <=> y)); + assert(testOrder(sm1, y.c_str(), x <=> y)); +#endif + } + + assert(testComparisons(x[0], sm2, string(1, x[0]) == y, string(1, x[0]) < y)); + assert(testComparisons(sm1, y[0], x == string(1, y[0]), x < string(1, y[0]))); +#if TEST_STD_VER > 17 + assert(testOrder(x[0], sm2, (string(1, x[0]) <=> y))); + assert(testOrder(sm1, y[0], x <=> (string(1, y[0])))); +#endif +} + +#if TEST_STD_VER > 17 +// Copied from constexpr_char_traits, but it doesn't have a full implementation. +// It has a comparison_category used in the tests. +template +struct char_traits { + using char_type = CharT; + using int_type = int; + using off_type = std::streamoff; + using pos_type = std::streampos; + using state_type = std::mbstate_t; + using comparison_category = Ordering; + + static constexpr void assign(char_type& c1, const char_type& c2) noexcept { c1 = c2; } + static constexpr bool eq(char_type c1, char_type c2) noexcept { return c1 == c2; } + static constexpr bool lt(char_type c1, char_type c2) noexcept { return c1 < c2; } + static constexpr int compare(const char_type* s1, const char_type* s2, std::size_t n) { + for (; n; --n, ++s1, ++s2) { + if (lt(*s1, *s2)) + return -1; + if (lt(*s2, *s1)) + return 1; } - assert((x[0] == sm2) == (string(1, x[0]) == y)); - assert((x[0] != sm2) == (string(1, x[0]) != y)); - assert((x[0] < sm2) == (string(1, x[0]) < y)); - assert((x[0] > sm2) == (string(1, x[0]) > y)); - assert((x[0] <= sm2) == (string(1, x[0]) <= y)); - assert((x[0] >= sm2) == (string(1, x[0]) >= y)); - assert((sm1 == y[0]) == (x == string(1, y[0]))); - assert((sm1 != y[0]) == (x != string(1, y[0]))); - assert((sm1 < y[0]) == (x < string(1, y[0]))); - assert((sm1 > y[0]) == (x > string(1, y[0]))); - assert((sm1 <= y[0]) == (x <= string(1, y[0]))); - assert((sm1 >= y[0]) == (x >= string(1, y[0]))); + return 0; + } + + static constexpr std::size_t length(const char_type* s); + static constexpr const char_type* find(const char_type* s, std::size_t n, const char_type& a); + static constexpr char_type* move(char_type* s1, const char_type* s2, std::size_t n); + static constexpr char_type* copy(char_type* s1, const char_type* s2, std::size_t n) { + char_type* r = s1; + for (; n; --n, ++s1, ++s2) + assign(*s1, *s2); + return r; + } + static constexpr char_type* assign(char_type* s, std::size_t n, char_type a) { + char_type* r = s; + for (; n; --n, ++s) + assign(*s, a); + return r; + } + static constexpr int_type not_eof(int_type c) noexcept { return eq_int_type(c, eof()) ? ~eof() : c; } + static constexpr char_type to_char_type(int_type c) noexcept { return char_type(c); } + static constexpr int_type to_int_type(char_type c) noexcept { return int_type(c); } + static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept { return c1 == c2; } + static constexpr int_type eof() noexcept { return int_type(EOF); } +}; + +template +constexpr void test() { + AssertOrderAreNoexcept(); + AssertOrderReturn(); + + using CharT = typename T::value_type; + + // sorted values + std::array s = [] { + std::array input{SV(""), SV("abc"), SV("abcdef")}; + return std::array{ + T{input[0].begin(), input[0].end()}, T{input[1].begin(), input[1].end()}, T{input[2].begin(), input[2].end()}}; + }(); + auto ctor = [](const T& string) { + std::sub_match sm; + sm.first = string.begin(); + sm.second = string.end(); + sm.matched = true; + return sm; + }; + std::array sm{ctor(s[0]), ctor(s[1]), ctor(s[2])}; + + for (std::size_t i = 0; i < s.size(); ++i) { + for (std::size_t j = 0; j < s.size(); ++j) { + assert(testOrder(s[i], sm[j], i == j ? Ordering::equivalent : i < j ? Ordering::less : Ordering::greater)); + } + } } +template +constexpr void test_all_orderings() { + test>(); // Strong ordering in its char_traits + test>, + std::weak_ordering>(); // No ordering in its char_traits + test>, std::weak_ordering>(); + test>, std::partial_ordering>(); +} +#endif // TEST_STD_VER > 17 + int main(int, char**) { test(std::string("123"), std::string("123")); test(std::string("1234"), std::string("123")); test(std::string("123\000" "56", 6), std::string("123\000" "56", 6), false); +#if TEST_STD_VER > 17 + test_all_orderings(); +#endif + #ifndef TEST_HAS_NO_WIDE_CHARACTERS test(std::wstring(L"123"), std::wstring(L"123")); test(std::wstring(L"1234"), std::wstring(L"123")); test(std::wstring(L"123\000" L"56", 6), std::wstring(L"123\000" L"56", 6), false); +#if TEST_STD_VER > 17 + test_all_orderings(); #endif +#endif // TEST_HAS_NO_WIDE_CHARACTERS return 0; }