Annotate more Abseil container methods with [[clang::lifetime_capture_by(...)]] and make them all forward to the non-captured overload This will allow catching some lifetime issues such as the following: ``` absl::btree_set<absl::string_view> s; s.insert(std::string(...)); // dangling ``` It also deduplicates the source of truth for each set of identical overloads. PiperOrigin-RevId: 736646161 Change-Id: I20453093d0c57c8a6ab080dc576c94a993147714
diff --git a/absl/container/BUILD.bazel b/absl/container/BUILD.bazel index f1026a5..37b5f2a 100644 --- a/absl/container/BUILD.bazel +++ b/absl/container/BUILD.bazel
@@ -659,11 +659,13 @@ copts = ABSL_DEFAULT_COPTS, linkopts = ABSL_DEFAULT_LINKOPTS, deps = [ + ":common_policy_traits", ":container_memory", ":raw_hash_set", "//absl/base:config", "//absl/base:core_headers", "//absl/base:throw_delegate", + "//absl/meta:type_traits", ], )
diff --git a/absl/container/CMakeLists.txt b/absl/container/CMakeLists.txt index 1419df7..39ff083 100644 --- a/absl/container/CMakeLists.txt +++ b/absl/container/CMakeLists.txt
@@ -720,9 +720,11 @@ ${ABSL_DEFAULT_COPTS} DEPS absl::config + absl::common_policy_traits absl::container_memory absl::core_headers absl::raw_hash_set + absl::type_traits absl::throw_delegate PUBLIC )
diff --git a/absl/container/internal/btree_container.h b/absl/container/internal/btree_container.h index a68ce44..551d621 100644 --- a/absl/container/internal/btree_container.h +++ b/absl/container/internal/btree_container.h
@@ -18,6 +18,7 @@ #include <algorithm> #include <initializer_list> #include <iterator> +#include <type_traits> #include <utility> #include "absl/base/attributes.h" @@ -451,6 +452,29 @@ template <class K> using key_arg = typename super_type::template key_arg<K>; + // NOTE: The mess here is to shorten the code for the (very repetitive) + // function overloads, and to allow the lifetime-bound overloads to dispatch + // to the non-lifetime-bound overloads, to ensure there is a single source of + // truth for each overload set. + // + // Enabled if an assignment from the given type would require the + // source object to remain alive for the life of the element. + // + // TODO(b/402804213): Remove these traits and simplify the overloads whenever + // we have a better mechanism available to handle lifetime analysis. + template <class K, bool Value, typename = void> + using LifetimeBoundK = + HasValue<Value, type_traits_internal::IsLifetimeBoundAssignment< + typename Tree::key_type, K>>; + template <class M, bool Value, typename = void> + using LifetimeBoundV = + HasValue<Value, type_traits_internal::IsLifetimeBoundAssignment< + typename Tree::params_type::mapped_type, M>>; + template <class K, bool KValue, class M, bool MValue, typename... Dummy> + using LifetimeBoundKV = + absl::conjunction<LifetimeBoundK<K, KValue, absl::void_t<Dummy...>>, + LifetimeBoundV<M, MValue>>; + public: using key_type = typename Tree::key_type; using mapped_type = typename params_type::mapped_type; @@ -464,85 +488,161 @@ using super_type::super_type; btree_map_container() {} + // TODO(b/402804213): Remove these macros whenever we have a better mechanism + // available to handle lifetime analysis. +#define ABSL_INTERNAL_X(Func, Callee, KQual, MQual, KValue, MValue, ...) \ + template < \ + typename K = key_type, class M, \ + ABSL_INTERNAL_IF_NOR( \ + KValue, MValue, \ + int = (EnableIf<LifetimeBoundKV<K, KValue, M, MValue, \ + IfRRef<int KQual>::AddPtr<K>, \ + IfRRef<int MQual>::AddPtr<M>>>()), \ + ABSL_INTERNAL_SINGLE_ARG( \ + int &..., \ + decltype(EnableIf<LifetimeBoundKV<K, KValue, M, MValue>>()) = \ + 0))> \ + decltype(auto) Func(__VA_ARGS__ key_arg<K> KQual k ABSL_INTERNAL_IF( \ + KValue, ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)), \ + M MQual obj ABSL_INTERNAL_IF( \ + MValue, ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this))) \ + ABSL_ATTRIBUTE_LIFETIME_BOUND { \ + return ABSL_INTERNAL_IF_OR(KValue, MValue, (this->template Func<K, M, 0>), \ + Callee)( \ + __VA_ARGS__ std::forward<decltype(k)>(k), \ + std::forward<decltype(obj)>(obj)); \ + } \ + friend struct std::enable_if<false> /* just to force a semicolon */ // Insertion routines. // Note: the nullptr template arguments and extra `const M&` overloads allow // for supporting bitfield arguments. - template <typename K = key_type, class M> - std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k, const M &obj) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_impl(k, obj); - } - template <typename K = key_type, class M, K * = nullptr> - std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, const M &obj) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_impl(std::forward<K>(k), obj); - } - template <typename K = key_type, class M, M * = nullptr> - std::pair<iterator, bool> insert_or_assign(const key_arg<K> &k, M &&obj) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_impl(k, std::forward<M>(obj)); - } - template <typename K = key_type, class M, K * = nullptr, M * = nullptr> - std::pair<iterator, bool> insert_or_assign(key_arg<K> &&k, M &&obj) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_impl(std::forward<K>(k), std::forward<M>(obj)); - } - template <typename K = key_type, class M> - iterator insert_or_assign(const_iterator hint, const key_arg<K> &k, - const M &obj) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_hint_impl(hint, k, obj); - } - template <typename K = key_type, class M, K * = nullptr> - iterator insert_or_assign(const_iterator hint, key_arg<K> &&k, - const M &obj) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_hint_impl(hint, std::forward<K>(k), obj); - } - template <typename K = key_type, class M, M * = nullptr> - iterator insert_or_assign(const_iterator hint, const key_arg<K> &k, - M &&obj) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_hint_impl(hint, k, std::forward<M>(obj)); - } - template <typename K = key_type, class M, K * = nullptr, M * = nullptr> - iterator insert_or_assign(const_iterator hint, key_arg<K> &&k, - M &&obj) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_hint_impl(hint, std::forward<K>(k), - std::forward<M>(obj)); - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, true); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, true); - template <typename K = key_type, typename... Args, - typename absl::enable_if_t< - !std::is_convertible<K, const_iterator>::value, int> = 0> - std::pair<iterator, bool> try_emplace(const key_arg<K> &k, Args &&...args) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return try_emplace_impl(k, std::forward<Args>(args)...); - } - template <typename K = key_type, typename... Args, - typename absl::enable_if_t< - !std::is_convertible<K, const_iterator>::value, int> = 0> - std::pair<iterator, bool> try_emplace(key_arg<K> &&k, Args &&...args) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...); - } - template <typename K = key_type, typename... Args> - iterator try_emplace(const_iterator hint, const key_arg<K> &k, - Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return try_emplace_hint_impl(hint, k, std::forward<Args>(args)...); - } - template <typename K = key_type, typename... Args> - iterator try_emplace(const_iterator hint, key_arg<K> &&k, - Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return try_emplace_hint_impl(hint, std::forward<K>(k), - std::forward<Args>(args)...); - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + true); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + true); - template <typename K = key_type> + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + true); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + true); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, + false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, true); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, false); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, true); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, + const &, false, false, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, + const &, false, true, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, + const &, true, false, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, + const &, true, true, + const_iterator(hint) ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, &&, + false, false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, &&, + false, true, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, &&, + true, false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, const &, &&, + true, true, const_iterator(hint) ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, const &, + false, false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, const &, + false, true, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, const &, + true, false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, const &, + true, true, const_iterator(hint) ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, &&, false, + false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, &&, false, + true, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, &&, true, + false, const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_hint_impl, &&, &&, true, + true, const_iterator(hint) ABSL_INTERNAL_COMMA); +#undef ABSL_INTERNAL_X + +#define ABSL_INTERNAL_X(Func, Callee, KQual, KValue, ...) \ + template <class K = key_type, \ + ABSL_INTERNAL_IF(KValue, class... Args, \ + int = EnableIf<LifetimeBoundK< \ + K, KValue, IfRRef<int KQual>::AddPtr<K>>>()), \ + ABSL_INTERNAL_IF( \ + KValue, \ + decltype(EnableIf<LifetimeBoundK< \ + K, KValue, IfRRef<int KQual>::AddPtr<K>>>()) = 0, \ + class... Args), \ + std::enable_if_t<!std::is_convertible<K, const_iterator>::value, \ + int> = 0> \ + decltype(auto) Func(__VA_ARGS__ key_arg<K> KQual k ABSL_INTERNAL_IF( \ + KValue, ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)), \ + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { \ + return ABSL_INTERNAL_IF(KValue, (this->template Func<K, 0>), Callee)( \ + __VA_ARGS__ std::forward<decltype(k)>(k), \ + std::forward<decltype(args)>(args)...); \ + } \ + friend struct std::enable_if<false> /* just to force a semicolon */ + ABSL_INTERNAL_X(try_emplace, try_emplace_impl, const &, false); + ABSL_INTERNAL_X(try_emplace, try_emplace_impl, const &, true); + ABSL_INTERNAL_X(try_emplace, try_emplace_impl, &&, false); + ABSL_INTERNAL_X(try_emplace, try_emplace_impl, &&, true); + ABSL_INTERNAL_X(try_emplace, try_emplace_hint_impl, const &, false, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(try_emplace, try_emplace_hint_impl, const &, true, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(try_emplace, try_emplace_hint_impl, &&, false, + const_iterator(hint) ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(try_emplace, try_emplace_hint_impl, &&, true, + const_iterator(hint) ABSL_INTERNAL_COMMA); +#undef ABSL_INTERNAL_X + + template <class K = key_type, int = EnableIf<LifetimeBoundK<K, false>>()> mapped_type &operator[](const key_arg<K> &k) ABSL_ATTRIBUTE_LIFETIME_BOUND { return try_emplace(k).first->second; } - template <typename K = key_type> + template <class K = key_type, int &..., EnableIf<LifetimeBoundK<K, true>> = 0> + mapped_type &operator[]( + const key_arg<K> &k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template operator[]<K, 0>(k); + } + template <class K = key_type, int = EnableIf<LifetimeBoundK<K, false>>()> mapped_type &operator[](key_arg<K> &&k) ABSL_ATTRIBUTE_LIFETIME_BOUND { return try_emplace(std::forward<K>(k)).first->second; } + template <class K = key_type, int &..., EnableIf<LifetimeBoundK<K, true>> = 0> + mapped_type &operator[](key_arg<K> &&k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY( + this)) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template operator[]<K, 0>(std::forward<K>(k)); + } template <typename K = key_type> mapped_type &at(const key_arg<K> &key) ABSL_ATTRIBUTE_LIFETIME_BOUND {
diff --git a/absl/container/internal/common.h b/absl/container/internal/common.h index 9239bb4..416e0e0 100644 --- a/absl/container/internal/common.h +++ b/absl/container/internal/common.h
@@ -21,10 +21,61 @@ #include "absl/meta/type_traits.h" #include "absl/types/optional.h" +// TODO(b/402804213): Clean up these macros when no longer needed. +#define ABSL_INTERNAL_SINGLE_ARG(...) __VA_ARGS__ + +#define ABSL_INTERNAL_IF_true(if_satisfied, ...) if_satisfied +#define ABSL_INTERNAL_IF_false(if_satisfied, ...) __VA_ARGS__ +#define ABSL_INTERNAL_IF(cond1, if_satisfied, ...) \ + ABSL_INTERNAL_IF_##cond1(if_satisfied, __VA_ARGS__) + +#define ABSL_INTERNAL_IF_true_AND_true ABSL_INTERNAL_IF_true +#define ABSL_INTERNAL_IF_false_AND_false ABSL_INTERNAL_IF_false +#define ABSL_INTERNAL_IF_true_AND_false ABSL_INTERNAL_IF_false_AND_false +#define ABSL_INTERNAL_IF_false_AND_true ABSL_INTERNAL_IF_false_AND_false +#define ABSL_INTERNAL_IF_AND(cond1, cond2, if_satisfied, ...) \ + ABSL_INTERNAL_IF_##cond1##_AND_##cond2(if_satisfied, __VA_ARGS__) + +#define ABSL_INTERNAL_IF_true_OR_true ABSL_INTERNAL_IF_true +#define ABSL_INTERNAL_IF_false_OR_false ABSL_INTERNAL_IF_false +#define ABSL_INTERNAL_IF_true_OR_false ABSL_INTERNAL_IF_true_OR_true +#define ABSL_INTERNAL_IF_false_OR_true ABSL_INTERNAL_IF_true_OR_true +#define ABSL_INTERNAL_IF_OR(cond1, cond2, if_satisfied, ...) \ + ABSL_INTERNAL_IF_##cond1##_OR_##cond2(if_satisfied, __VA_ARGS__) + +#define ABSL_INTERNAL_IF_true_NOR_true ABSL_INTERNAL_IF_false_AND_false +#define ABSL_INTERNAL_IF_false_NOR_false ABSL_INTERNAL_IF_true_AND_true +#define ABSL_INTERNAL_IF_true_NOR_false ABSL_INTERNAL_IF_false_AND_true +#define ABSL_INTERNAL_IF_false_NOR_true ABSL_INTERNAL_IF_true_AND_false +#define ABSL_INTERNAL_IF_NOR(cond1, cond2, if_satisfied, ...) \ + ABSL_INTERNAL_IF_##cond1##_NOR_##cond2(if_satisfied, __VA_ARGS__) + +#define ABSL_INTERNAL_COMMA , + namespace absl { ABSL_NAMESPACE_BEGIN namespace container_internal { +// TODO(b/402804213): Clean up these traits when no longer needed or +// deduplicate them with absl::functional_internal::EnableIf. +template <class Cond> +using EnableIf = std::enable_if_t<Cond::value, int>; + +template <bool Value, class T> +using HasValue = std::conditional_t<Value, T, absl::negation<T>>; + +template <class T> +struct IfRRef { + template <class Other> + using AddPtr = Other; +}; + +template <class T> +struct IfRRef<T&&> { + template <class Other> + using AddPtr = Other*; +}; + template <class, class = void> struct IsTransparent : std::false_type {}; template <class T>
diff --git a/absl/container/internal/raw_hash_map.h b/absl/container/internal/raw_hash_map.h index 8ca0849..a3ec0bb 100644 --- a/absl/container/internal/raw_hash_map.h +++ b/absl/container/internal/raw_hash_map.h
@@ -22,8 +22,10 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" #include "absl/base/internal/throw_delegate.h" +#include "absl/container/internal/common_policy_traits.h" #include "absl/container/internal/container_memory.h" #include "absl/container/internal/raw_hash_set.h" // IWYU pragma: export +#include "absl/meta/type_traits.h" namespace absl { ABSL_NAMESPACE_BEGIN @@ -48,6 +50,31 @@ typename KeyArg<IsTransparent<Eq>::value && IsTransparent<Hash>::value>:: template type<K, typename Policy::key_type>; + // NOTE: The mess here is to shorten the code for the (very repetitive) + // function overloads, and to allow the lifetime-bound overloads to dispatch + // to the non-lifetime-bound overloads, to ensure there is a single source of + // truth for each overload set. + // + // Enabled if an assignment from the given type would require the + // source object to remain alive for the life of the element. + // + // TODO(b/402804213): Remove these traits and simplify the overloads whenever + // we have a better mechanism available to handle lifetime analysis. + template <class K, bool Value, typename = void> + using LifetimeBoundK = HasValue< + Value, std::conditional_t<policy_trait_element_is_owner<Policy>::value, + std::false_type, + type_traits_internal::IsLifetimeBoundAssignment< + typename Policy::key_type, K>>>; + template <class V, bool Value, typename = void> + using LifetimeBoundV = + HasValue<Value, type_traits_internal::IsLifetimeBoundAssignment< + typename Policy::mapped_type, V>>; + template <class K, bool KValue, class V, bool VValue, typename... Dummy> + using LifetimeBoundKV = + absl::conjunction<LifetimeBoundK<K, KValue, absl::void_t<Dummy...>>, + LifetimeBoundV<V, VValue>>; + public: using key_type = typename Policy::key_type; using mapped_type = typename Policy::mapped_type; @@ -71,87 +98,176 @@ // union { int n : 1; }; // flat_hash_map<int, int> m; // m.insert_or_assign(n, n); - template <class K = key_type, class V = mapped_type, K* = nullptr, - V* = nullptr> - std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, V&& v) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_impl(std::forward<K>(k), std::forward<V>(v)); - } + // + // TODO(b/402804213): Remove these macros whenever we have a better mechanism + // available to handle lifetime analysis. +#define ABSL_INTERNAL_X(Func, Callee, KQual, VQual, KValue, VValue, Tail, ...) \ + template < \ + typename K = key_type, class V = mapped_type, \ + ABSL_INTERNAL_IF_NOR( \ + KValue, VValue, \ + int = (EnableIf<LifetimeBoundKV<K, KValue, V, VValue, \ + IfRRef<int KQual>::AddPtr<K>, \ + IfRRef<int VQual>::AddPtr<V>>>()), \ + ABSL_INTERNAL_SINGLE_ARG( \ + int &..., \ + decltype(EnableIf<LifetimeBoundKV<K, KValue, V, VValue>>()) = \ + 0))> \ + decltype(auto) Func(__VA_ARGS__ key_arg<K> KQual k ABSL_INTERNAL_IF( \ + KValue, ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)), \ + V VQual v ABSL_INTERNAL_IF( \ + VValue, ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this))) \ + ABSL_ATTRIBUTE_LIFETIME_BOUND { \ + return ABSL_INTERNAL_IF_OR(KValue, VValue, (this->template Func<K, V, 0>), \ + Callee)(std::forward<decltype(k)>(k), \ + std::forward<decltype(v)>(v)) Tail; \ + } \ + static_assert(true, "This is to force a semicolon.") - template <class K = key_type, class V = mapped_type, K* = nullptr> - std::pair<iterator, bool> insert_or_assign(key_arg<K>&& k, const V& v) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_impl(std::forward<K>(k), v); - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, true, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, true, ABSL_INTERNAL_SINGLE_ARG()); - template <class K = key_type, class V = mapped_type, V* = nullptr> - std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, V&& v) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_impl(k, std::forward<V>(v)); - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + true, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + true, ABSL_INTERNAL_SINGLE_ARG()); - template <class K = key_type, class V = mapped_type> - std::pair<iterator, bool> insert_or_assign(const key_arg<K>& k, const V& v) - ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign_impl(k, v); - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + true, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + false, ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + true, ABSL_INTERNAL_SINGLE_ARG()); - template <class K = key_type, class V = mapped_type, K* = nullptr, - V* = nullptr> - iterator insert_or_assign(const_iterator, key_arg<K>&& k, - V&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign(std::forward<K>(k), std::forward<V>(v)).first; - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, false, + ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, true, + ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, false, + ABSL_INTERNAL_SINGLE_ARG()); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, true, + ABSL_INTERNAL_SINGLE_ARG()); - template <class K = key_type, class V = mapped_type, K* = nullptr> - iterator insert_or_assign(const_iterator, key_arg<K>&& k, - const V& v) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign(std::forward<K>(k), v).first; - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + false, true, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, const &, + true, true, .first, const_iterator ABSL_INTERNAL_COMMA); - template <class K = key_type, class V = mapped_type, V* = nullptr> - iterator insert_or_assign(const_iterator, const key_arg<K>& k, - V&& v) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign(k, std::forward<V>(v)).first; - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, false, + true, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, const &, &&, true, + true, .first, const_iterator ABSL_INTERNAL_COMMA); - template <class K = key_type, class V = mapped_type> - iterator insert_or_assign(const_iterator, const key_arg<K>& k, - const V& v) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert_or_assign(k, v).first; - } + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, false, + true, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + false, .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, const &, true, + true, .first, const_iterator ABSL_INTERNAL_COMMA); + + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, false, + .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, false, true, + .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, false, + .first, const_iterator ABSL_INTERNAL_COMMA); + ABSL_INTERNAL_X(insert_or_assign, insert_or_assign_impl, &&, &&, true, true, + .first, const_iterator ABSL_INTERNAL_COMMA); +#undef ABSL_INTERNAL_X // All `try_emplace()` overloads make the same guarantees regarding rvalue // arguments as `std::unordered_map::try_emplace()`, namely that these // functions will not move from rvalue arguments if insertions do not happen. - template <class K = key_type, class... Args, + template <class K = key_type, int = EnableIf<LifetimeBoundK<K, false, K *>>(), + class... Args, typename std::enable_if< - !std::is_convertible<K, const_iterator>::value, int>::type = 0, - K* = nullptr> - std::pair<iterator, bool> try_emplace(key_arg<K>&& k, Args&&... args) + !std::is_convertible<K, const_iterator>::value, int>::type = 0> + std::pair<iterator, bool> try_emplace(key_arg<K> &&k, Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { return try_emplace_impl(std::forward<K>(k), std::forward<Args>(args)...); } template <class K = key_type, class... Args, + EnableIf<LifetimeBoundK<K, true, K *>> = 0, typename std::enable_if< !std::is_convertible<K, const_iterator>::value, int>::type = 0> - std::pair<iterator, bool> try_emplace(const key_arg<K>& k, Args&&... args) + std::pair<iterator, bool> try_emplace( + key_arg<K> &&k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this), + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template try_emplace<K, 0>(std::forward<K>(k), + std::forward<Args>(args)...); + } + + template <class K = key_type, int = EnableIf<LifetimeBoundK<K, false>>(), + class... Args, + typename std::enable_if< + !std::is_convertible<K, const_iterator>::value, int>::type = 0> + std::pair<iterator, bool> try_emplace(const key_arg<K> &k, Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { return try_emplace_impl(k, std::forward<Args>(args)...); } - - template <class K = key_type, class... Args, K* = nullptr> - iterator try_emplace(const_iterator, key_arg<K>&& k, - Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return try_emplace(std::forward<K>(k), std::forward<Args>(args)...).first; + template <class K = key_type, class... Args, + EnableIf<LifetimeBoundK<K, true>> = 0, + typename std::enable_if< + !std::is_convertible<K, const_iterator>::value, int>::type = 0> + std::pair<iterator, bool> try_emplace( + const key_arg<K> &k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this), + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template try_emplace<K, 0>(k, std::forward<Args>(args)...); } - template <class K = key_type, class... Args> - iterator try_emplace(const_iterator, const key_arg<K>& k, - Args&&... args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + template <class K = key_type, int = EnableIf<LifetimeBoundK<K, false, K *>>(), + class... Args> + iterator try_emplace(const_iterator, key_arg<K> &&k, + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return try_emplace(std::forward<K>(k), std::forward<Args>(args)...).first; + } + template <class K = key_type, class... Args, + EnableIf<LifetimeBoundK<K, true, K *>> = 0> + iterator try_emplace(const_iterator hint, + key_arg<K> &&k ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this), + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template try_emplace<K, 0>(hint, std::forward<K>(k), + std::forward<Args>(args)...); + } + + template <class K = key_type, int = EnableIf<LifetimeBoundK<K, false>>(), + class... Args> + iterator try_emplace(const_iterator, const key_arg<K> &k, + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { return try_emplace(k, std::forward<Args>(args)...).first; } + template <class K = key_type, class... Args, + EnableIf<LifetimeBoundK<K, true>> = 0> + iterator try_emplace(const_iterator hint, + const key_arg<K> &k + ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this), + Args &&...args) ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template try_emplace<K, 0>(hint, std::forward<K>(k), + std::forward<Args>(args)...); + } template <class K = key_type, class P = Policy> MappedReference<P> at(const key_arg<K>& key) ABSL_ATTRIBUTE_LIFETIME_BOUND { @@ -174,8 +290,9 @@ return Policy::value(&*it); } - template <class K = key_type, class P = Policy, K* = nullptr> - MappedReference<P> operator[](key_arg<K>&& key) + template <class K = key_type, class P = Policy, + int = EnableIf<LifetimeBoundK<K, false, K *>>()> + MappedReference<P> operator[](key_arg<K> &&key) ABSL_ATTRIBUTE_LIFETIME_BOUND { // It is safe to use unchecked_deref here because try_emplace // will always return an iterator pointing to a valid item in the table, @@ -183,15 +300,30 @@ return Policy::value( &this->unchecked_deref(try_emplace(std::forward<K>(key)).first)); } + template <class K = key_type, class P = Policy, int &..., + EnableIf<LifetimeBoundK<K, true, K *>> = 0> + MappedReference<P> operator[]( + key_arg<K> &&key ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template operator[]<K, P, 0>(std::forward<K>(key)); + } - template <class K = key_type, class P = Policy> - MappedReference<P> operator[](const key_arg<K>& key) + template <class K = key_type, class P = Policy, + int = EnableIf<LifetimeBoundK<K, false>>()> + MappedReference<P> operator[](const key_arg<K> &key) ABSL_ATTRIBUTE_LIFETIME_BOUND { // It is safe to use unchecked_deref here because try_emplace // will always return an iterator pointing to a valid item in the table, // since it inserts if nothing is found for the given key. return Policy::value(&this->unchecked_deref(try_emplace(key).first)); } + template <class K = key_type, class P = Policy, int &..., + EnableIf<LifetimeBoundK<K, true>> = 0> + MappedReference<P> operator[]( + const key_arg<K> &key ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template operator[]<K, P, 0>(key); + } private: template <class K, class V>
diff --git a/absl/container/internal/raw_hash_set.h b/absl/container/internal/raw_hash_set.h index 41a480a..fdfb822 100644 --- a/absl/container/internal/raw_hash_set.h +++ b/absl/container/internal/raw_hash_set.h
@@ -2495,15 +2495,15 @@ // flat_hash_map<std::string, int> m; // m.insert(std::make_pair("abc", 42)); template <class T, - std::enable_if_t<IsDecomposableAndInsertable<T>::value && - IsNotBitField<T>::value && - !IsLifetimeBoundAssignmentFrom<T>::value, - int> = 0> + int = std::enable_if_t<IsDecomposableAndInsertable<T>::value && + IsNotBitField<T>::value && + !IsLifetimeBoundAssignmentFrom<T>::value, + int>()> std::pair<iterator, bool> insert(T&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND { return emplace(std::forward<T>(value)); } - template <class T, + template <class T, int&..., std::enable_if_t<IsDecomposableAndInsertable<T>::value && IsNotBitField<T>::value && IsLifetimeBoundAssignmentFrom<T>::value, @@ -2511,7 +2511,7 @@ std::pair<iterator, bool> insert( T&& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return emplace(std::forward<T>(value)); + return this->template insert<T, 0>(std::forward<T>(value)); } // This overload kicks in when the argument is a bitfield or an lvalue of @@ -2525,22 +2525,22 @@ // const char* p = "hello"; // s.insert(p); // - template <class T, std::enable_if_t< + template <class T, int = std::enable_if_t< IsDecomposableAndInsertable<const T&>::value && !IsLifetimeBoundAssignmentFrom<const T&>::value, - int> = 0> + int>()> std::pair<iterator, bool> insert(const T& value) ABSL_ATTRIBUTE_LIFETIME_BOUND { return emplace(value); } - template <class T, + template <class T, int&..., std::enable_if_t<IsDecomposableAndInsertable<const T&>::value && IsLifetimeBoundAssignmentFrom<const T&>::value, int> = 0> std::pair<iterator, bool> insert( const T& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return emplace(value); + return this->template insert<T, 0>(value); } // This overload kicks in when the argument is an rvalue of init_type. Its @@ -2567,21 +2567,22 @@ #endif template <class T, - std::enable_if_t<IsDecomposableAndInsertable<T>::value && - IsNotBitField<T>::value && - !IsLifetimeBoundAssignmentFrom<T>::value, - int> = 0> + int = std::enable_if_t<IsDecomposableAndInsertable<T>::value && + IsNotBitField<T>::value && + !IsLifetimeBoundAssignmentFrom<T>::value, + int>()> iterator insert(const_iterator, T&& value) ABSL_ATTRIBUTE_LIFETIME_BOUND { return insert(std::forward<T>(value)).first; } - template <class T, + template <class T, int&..., std::enable_if_t<IsDecomposableAndInsertable<T>::value && IsNotBitField<T>::value && IsLifetimeBoundAssignmentFrom<T>::value, int> = 0> - iterator insert(const_iterator, T&& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY( - this)) ABSL_ATTRIBUTE_LIFETIME_BOUND { - return insert(std::forward<T>(value)).first; + iterator insert(const_iterator hint, + T&& value ABSL_INTERNAL_ATTRIBUTE_CAPTURED_BY(this)) + ABSL_ATTRIBUTE_LIFETIME_BOUND { + return this->template insert<T, 0>(hint, std::forward<T>(value)); } template <class T, std::enable_if_t<