[llvm-branch-commits] [libcxx] release/23.x: [libc++] Opt `std::*set` out of map key extraction optimization (#220452) (PR #222641)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Sep 10 06:02:15 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: llvmbot
<details>
<summary>Changes</summary>
Backport 10d4bdf93a82fb290b8c55da6ed19ff54da67296
Requested by: @<!-- -->philnik777
---
Full diff: https://github.com/llvm/llvm-project/pull/222641.diff
5 Files Affected:
- (modified) libcxx/include/__hash_table (+1-1)
- (modified) libcxx/include/__tree (+3-3)
- (modified) libcxx/include/__utility/try_key_extraction.h (+10-6)
- (modified) libcxx/test/std/containers/associative/set/emplace.pass.cpp (+16)
- (modified) libcxx/test/std/containers/unord/unord.set/emplace.pass.cpp (+10)
``````````diff
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 0ce4cf3edd6fd..b5a79e74eb539 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -788,7 +788,7 @@ public:
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __emplace_unique(_Args&&... __args) {
- return std::__try_key_extraction<key_type>(
+ return std::__try_key_extraction<key_type, value_type>(
[this](const key_type& __key, _Args&&... __args2) {
size_t __hash = hash_function()(__key);
size_type __bc = bucket_count();
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index dafeff3e2e019..ef2c702a1ff4b 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1074,7 +1074,7 @@ public:
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool> __emplace_unique(_Args&&... __args) {
- return std::__try_key_extraction<key_type>(
+ return std::__try_key_extraction<key_type, value_type>(
[this](const key_type& __key, _Args&&... __args2) {
auto [__parent, __child] = __find_equal(__key);
__node_pointer __r = std::__static_fancy_pointer_cast<__node_pointer>(__child);
@@ -1105,7 +1105,7 @@ public:
template <class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 pair<iterator, bool>
__emplace_hint_unique(const_iterator __p, _Args&&... __args) {
- return std::__try_key_extraction<key_type>(
+ return std::__try_key_extraction<key_type, value_type>(
[this, __p](const key_type& __key, _Args&&... __args2) {
__node_base_pointer __dummy;
auto [__parent, __child] = __find_equal(__p, __dummy, __key);
@@ -1182,7 +1182,7 @@ public:
using __reference = decltype(*__first);
for (; __first != __last; ++__first) {
- std::__try_key_extraction<key_type>(
+ std::__try_key_extraction<key_type, value_type>(
[this, &__max_node](const key_type& __key, __reference&& __val) {
if (value_comp()(__max_node->__get_value(), __key)) { // __key > __max_node
__node_holder __nd = __construct_node(std::forward<__reference>(__val));
diff --git a/libcxx/include/__utility/try_key_extraction.h b/libcxx/include/__utility/try_key_extraction.h
index 3423d746dee7f..f645036d730dd 100644
--- a/libcxx/include/__utility/try_key_extraction.h
+++ b/libcxx/include/__utility/try_key_extraction.h
@@ -27,13 +27,14 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <class _KeyT, class _Ret, class _WithKey, class _WithoutKey, class... _Args>
+template <class _KeyT, bool, class _Ret, class _WithKey, class _WithoutKey, class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
__try_key_extraction_impl(__priority_tag<0>, _WithKey, _WithoutKey __without_key, _Args&&... __args) {
return __without_key(std::forward<_Args>(__args)...);
}
template <class _KeyT,
+ bool,
class _Ret,
class _WithKey,
class _WithoutKey,
@@ -45,11 +46,12 @@ __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _
}
template <class _KeyT,
+ bool __is_map,
class _Ret,
class _WithKey,
class _WithoutKey,
class _Arg,
- __enable_if_t<__is_pair_v<__remove_const_ref_t<_Arg> > &&
+ __enable_if_t<__is_map && __is_pair_v<__remove_const_ref_t<_Arg> > &&
is_same<__remove_const_t<typename __remove_const_ref_t<_Arg>::first_type>, _KeyT>::value,
int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
@@ -58,12 +60,13 @@ __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _
}
template <class _KeyT,
+ bool __is_map,
class _Ret,
class _WithKey,
class _WithoutKey,
class _Arg1,
class _Arg2,
- __enable_if_t<is_same<_KeyT, __remove_const_ref_t<_Arg1> >::value, int> = 0>
+ __enable_if_t<__is_map && is_same<_KeyT, __remove_const_ref_t<_Arg1> >::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret
__try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _Arg1&& __arg1, _Arg2&& __arg2) {
return __with_key(__arg1, std::forward<_Arg1>(__arg1), std::forward<_Arg2>(__arg2));
@@ -71,13 +74,14 @@ __try_key_extraction_impl(__priority_tag<1>, _WithKey __with_key, _WithoutKey, _
#ifndef _LIBCPP_CXX03_LANG
template <class _KeyT,
+ bool __is_map,
class _Ret,
class _WithKey,
class _WithoutKey,
class _PiecewiseConstruct,
class _Tuple1,
class _Tuple2,
- __enable_if_t<is_same<__remove_const_ref_t<_PiecewiseConstruct>, piecewise_construct_t>::value &&
+ __enable_if_t<__is_map && is_same<__remove_const_ref_t<_PiecewiseConstruct>, piecewise_construct_t>::value &&
__is_tuple_v<_Tuple1> && tuple_size<_Tuple1>::value == 1 &&
is_same<__remove_const_ref_t<typename tuple_element<0, _Tuple1>::type>, _KeyT>::value,
int> = 0>
@@ -101,11 +105,11 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Ret __try_key_extraction_im
// arguments. Otherwise it calls the `__without_key` function with all of the arguments.
//
// Both `__with_key` and `__without_key` must take all arguments by reference.
-template <class _KeyT, class _WithKey, class _WithoutKey, class... _Args>
+template <class _KeyT, class _ValT, class _WithKey, class _WithoutKey, class... _Args>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 decltype(std::declval<_WithoutKey>()(std::declval<_Args>()...))
__try_key_extraction(_WithKey __with_key, _WithoutKey __without_key, _Args&&... __args) {
using _Ret = decltype(__without_key(std::forward<_Args>(__args)...));
- return std::__try_key_extraction_impl<_KeyT, _Ret>(
+ return std::__try_key_extraction_impl<_KeyT, !is_same<_KeyT, _ValT>::value, _Ret>(
__priority_tag<1>(), __with_key, __without_key, std::forward<_Args>(__args)...);
}
diff --git a/libcxx/test/std/containers/associative/set/emplace.pass.cpp b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
index 55ef489d2a6ba..d2863be2eaa00 100644
--- a/libcxx/test/std/containers/associative/set/emplace.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/emplace.pass.cpp
@@ -92,6 +92,22 @@ TEST_CONSTEXPR_CXX26 bool test() {
assert(std::get<1>(res));
assert(set.begin() == std::get<0>(res));
}
+ { // Regression test for https://llvm.org/PR220451.
+ // Make sure emplace with multiple arguments doesn't extract the first argument as a key for sets.
+ struct S {
+ const int val;
+ TEST_CONSTEXPR explicit S(int v) : val(v) {}
+ TEST_CONSTEXPR S(const S& s, int offset) : val(s.val + offset) {}
+ TEST_CONSTEXPR bool operator<(const S& other) const { return val < other.val; }
+ TEST_CONSTEXPR bool operator==(const S& other) const { return val == other.val; }
+ };
+ std::set<S> s;
+ s.emplace(2);
+ auto res = s.emplace(S(1), 1);
+ assert(!res.second);
+ assert(s.size() == 1);
+ assert(s.begin()->val == 2);
+ }
return true;
}
diff --git a/libcxx/test/std/containers/unord/unord.set/emplace.pass.cpp b/libcxx/test/std/containers/unord/unord.set/emplace.pass.cpp
index 24e85a1ab7f54..daea16be51cef 100644
--- a/libcxx/test/std/containers/unord/unord.set/emplace.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/emplace.pass.cpp
@@ -18,6 +18,7 @@
// pair<iterator, bool> emplace(Args&&... args);
#include <cassert>
+#include <string>
#include <unordered_set>
#include "../../Emplaceable.h"
@@ -75,6 +76,15 @@ int main(int, char**) {
assert(std::get<1>(res));
assert(set.begin() == std::get<0>(res));
}
+ { // Regression test for https://llvm.org/PR220451.
+ // Make sure emplace with multiple arguments doesn't extract the first argument as a key for unordered sets.
+ std::unordered_set<std::string> s;
+ s.emplace("foo");
+ auto res = s.emplace(std::string("ofoo"), 1);
+ assert(!res.second);
+ assert(s.size() == 1);
+ assert(*s.begin() == "foo");
+ }
return 0;
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/222641
More information about the llvm-branch-commits
mailing list