[libc++] Fix uses of non-empty transparent comparator in <map> (#152624)

The `__get_value()` member function was removed in LLVM 21, but the
calls in `<map>` weren't removed. This patch completes the removal and
adds regression test cases.

Fixes #152543.

(cherry picked from commit 7ae1424286203e37f6238e9349b1bfbe873ebabf)
This commit is contained in:
A. Jiang 2025-08-09 02:41:06 +08:00 committed by Tobias Hieta
parent ba87d05c55
commit c092e2af14
No known key found for this signature in database
GPG Key ID: 44F2485E45D59042
12 changed files with 65 additions and 2 deletions

View File

@ -692,12 +692,12 @@ public:
# if _LIBCPP_STD_VER >= 14
template <typename _K2>
_LIBCPP_HIDE_FROM_ABI bool operator()(const _K2& __x, const _CP& __y) const {
return __comp_(__x, __y.__get_value().first);
return __comp_(__x, __y.first);
}
template <typename _K2>
_LIBCPP_HIDE_FROM_ABI bool operator()(const _CP& __x, const _K2& __y) const {
return __comp_(__x.__get_value().first, __y);
return __comp_(__x.first, __y);
}
# endif
};

View File

@ -33,6 +33,10 @@ int main(int, char**) {
typedef std::map<int, double, transparent_less_not_referenceable> M;
assert(M().count(C2Int{5}) == 0);
}
{
using M = std::map<int, double, transparent_less_nonempty>;
assert(M().count(C2Int{5}) == 0);
}
return 0;
}

View File

@ -40,6 +40,13 @@ int main(int, char**) {
P result = example.equal_range(C2Int{5});
assert(result.first == result.second);
}
{
using M = std::map<int, double, transparent_less_nonempty>;
using P = std::pair<typename M::iterator, typename M::iterator>;
M example;
P result = example.equal_range(C2Int{5});
assert(result.first == result.second);
}
return 0;
}

View File

@ -36,6 +36,11 @@ int main(int, char**) {
M example;
assert(example.find(C2Int{5}) == example.end());
}
{
using M = std::map<int, double, transparent_less_nonempty>;
M example;
assert(example.find(C2Int{5}) == example.end());
}
return 0;
}

View File

@ -36,6 +36,11 @@ int main(int, char**) {
M example;
assert(example.lower_bound(C2Int{5}) == example.end());
}
{
using M = std::map<int, double, transparent_less_nonempty>;
M example;
assert(example.lower_bound(C2Int{5}) == example.end());
}
return 0;
}

View File

@ -36,6 +36,11 @@ int main(int, char**) {
M example;
assert(example.upper_bound(C2Int{5}) == example.end());
}
{
using M = std::map<int, double, transparent_less_nonempty>;
M example;
assert(example.upper_bound(C2Int{5}) == example.end());
}
return 0;
}

View File

@ -33,6 +33,10 @@ int main(int, char**) {
typedef std::multimap<int, double, transparent_less_not_referenceable> M;
assert(M().count(C2Int{5}) == 0);
}
{
using M = std::multimap<int, double, transparent_less_nonempty>;
assert(M().count(C2Int{5}) == 0);
}
return 0;
}

View File

@ -40,6 +40,13 @@ int main(int, char**) {
P result = example.equal_range(C2Int{5});
assert(result.first == result.second);
}
{
using M = std::multimap<int, double, transparent_less_nonempty>;
using P = std::pair<typename M::iterator, typename M::iterator>;
M example;
P result = example.equal_range(C2Int{5});
assert(result.first == result.second);
}
return 0;
}

View File

@ -36,6 +36,11 @@ int main(int, char**) {
M example;
assert(example.find(C2Int{5}) == example.end());
}
{
using M = std::multimap<int, double, transparent_less_nonempty>;
M example;
assert(example.find(C2Int{5}) == example.end());
}
return 0;
}

View File

@ -36,6 +36,11 @@ int main(int, char**) {
M example;
assert(example.lower_bound(C2Int{5}) == example.end());
}
{
using M = std::multimap<int, double, transparent_less_nonempty>;
M example;
assert(example.lower_bound(C2Int{5}) == example.end());
}
return 0;
}

View File

@ -36,6 +36,11 @@ int main(int, char**) {
M example;
assert(example.upper_bound(C2Int{5}) == example.end());
}
{
using M = std::multimap<int, double, transparent_less_nonempty>;
M example;
assert(example.upper_bound(C2Int{5}) == example.end());
}
return 0;
}

View File

@ -36,6 +36,17 @@ struct transparent_less_not_referenceable
using is_transparent = void () const &; // it's a type; a weird one, but a type
};
// Prevent regression when empty base class optimization is not suitable.
// See https://github.com/llvm/llvm-project/issues/152543.
struct transparent_less_nonempty {
template <class T, class U>
constexpr bool operator()(T&& t, U&& u) const {
return std::forward<T>(t) < std::forward<U>(u);
}
struct is_transparent {
} pad_; // making this comparator non-empty
};
struct transparent_less_no_type
{
template <class T, class U>