aboutsummaryrefslogtreecommitdiff
path: root/absl/meta
diff options
context:
space:
mode:
authorArthur O'Dwyer <arthur.j.odwyer@gmail.com>2024-02-17 09:32:01 -0800
committerCopybara-Service <copybara-worker@google.com>2024-02-17 09:33:19 -0800
commit14b8a4eac3e5a7b97ba4cc7b7dadf2a85aae8215 (patch)
tree53e0c8bbe0041bd92e08ea44bf5e658e676d26cc /absl/meta
parent8a3caf7dea955b513a6c1b572a2423c6b4213402 (diff)
downloadabseil-14b8a4eac3e5a7b97ba4cc7b7dadf2a85aae8215.tar.gz
abseil-14b8a4eac3e5a7b97ba4cc7b7dadf2a85aae8215.tar.bz2
abseil-14b8a4eac3e5a7b97ba4cc7b7dadf2a85aae8215.zip
PR #1625: absl::is_trivially_relocatable now respects assignment operators
Imported from GitHub PR https://github.com/abseil/abseil-cpp/pull/1625 Trivial relocatability also requires that the type not do anything weird with its assignment operator; update the type-trait to reflect this. (This is the definition used by BSL, Folly, HPX, Thrust, Parlay, Amadeus, and P1144.) This is important if we want to use `absl::is_trivially_relocatable` as a gate for memcpy optimizations in `inlined_vector::erase` and/or `inlined_vector::swap`, because in those cases relocation is used to replace part of a sequence involving assignment; the optimization requires an assignment operator that behaves value-semantically. Clang's builtin currently fails to check the assignment operator, so we stop using it entirely for now. We already refused to use it on Win32, Win64, and Apple, for various unrelated reasons. I'm working on giving Clang's builtin the behavior that would let us re-enable it here. Assume that any compiler providing both `__cpp_impl_trivially_relocatable` and a builtin `__is_trivially_relocatable(T)` will use the appropriate (P1144) definition for its builtin. Right now there's only one such compiler (the P1144 reference implementation, which forks Clang), so this is largely a moot point, but I'm being optimistic. Merge d943abdbabc1b7080aa5f0a2fff3e724135164dc into 34604d5b1f6ae14c65b3992478b59f7108051979 Merging this change closes #1625 COPYBARA_INTEGRATE_REVIEW=https://github.com/abseil/abseil-cpp/pull/1625 from Quuxplusone:trivially-relocatable d943abdbabc1b7080aa5f0a2fff3e724135164dc PiperOrigin-RevId: 607977323 Change-Id: I6436a60326c6d1064bdd71ec2e15b86b7a29efd4
Diffstat (limited to 'absl/meta')
-rw-r--r--absl/meta/type_traits.h50
-rw-r--r--absl/meta/type_traits_test.cc59
2 files changed, 70 insertions, 39 deletions
diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h
index a456ae4f..ed5e6080 100644
--- a/absl/meta/type_traits.h
+++ b/absl/meta/type_traits.h
@@ -152,8 +152,8 @@ template <typename... Ts>
struct disjunction : std::false_type {};
template <typename T, typename... Ts>
-struct disjunction<T, Ts...> :
- std::conditional<T::value, T, disjunction<Ts...>>::type {};
+struct disjunction<T, Ts...>
+ : std::conditional<T::value, T, disjunction<Ts...>>::type {};
template <typename T>
struct disjunction<T> : T {};
@@ -315,22 +315,23 @@ using common_type_t = typename std::common_type<T...>::type;
template <typename T>
using underlying_type_t = typename std::underlying_type<T>::type;
-
namespace type_traits_internal {
#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
(defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
// std::result_of is deprecated (C++17) or removed (C++20)
-template<typename> struct result_of;
-template<typename F, typename... Args>
+template <typename>
+struct result_of;
+template <typename F, typename... Args>
struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
#else
-template<typename F> using result_of = std::result_of<F>;
+template <typename F>
+using result_of = std::result_of<F>;
#endif
} // namespace type_traits_internal
-template<typename F>
+template <typename F>
using result_of_t = typename type_traits_internal::result_of<F>::type;
namespace type_traits_internal {
@@ -463,20 +464,23 @@ namespace type_traits_internal {
// Make the swap-related traits/function accessible from this namespace.
using swap_internal::IsNothrowSwappable;
using swap_internal::IsSwappable;
-using swap_internal::Swap;
using swap_internal::StdSwapIsUnconstrained;
+using swap_internal::Swap;
} // namespace type_traits_internal
// absl::is_trivially_relocatable<T>
//
// Detects whether a type is known to be "trivially relocatable" -- meaning it
-// can be relocated without invoking the constructor/destructor, using a form of
-// move elision.
+// can be relocated from one place to another as if by memcpy/memmove.
+// This implies that its object representation doesn't depend on its address,
+// and also none of its special member functions do anything strange.
//
-// This trait is conservative, for backwards compatibility. If it's true then
-// the type is definitely trivially relocatable, but if it's false then the type
-// may or may not be.
+// This trait is conservative. If it's true then the type is definitely
+// trivially relocatable, but if it's false then the type may or may not be. For
+// example, std::vector<int> is trivially relocatable on every known STL
+// implementation, but absl::is_trivially_relocatable<std::vector<int>> remains
+// false.
//
// Example:
//
@@ -509,22 +513,26 @@ using swap_internal::StdSwapIsUnconstrained;
// TODO(b/324278148): If all versions we use have the bug fixed, then
// remove the condition.
//
+// Clang on all platforms fails to detect that a type with a user-provided
+// move-assignment operator is not trivially relocatable. So in fact we
+// opt out of Clang altogether, for now.
+//
+// TODO(b/325479096): Remove the opt-out once Clang's behavior is fixed.
+//
// According to https://github.com/abseil/abseil-cpp/issues/1479, this does not
// work with NVCC either.
-#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
- !(defined(__clang__) && (defined(_WIN32) || defined(_WIN64))) && \
- !(defined(__APPLE__)) && !defined(__NVCC__)
+#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
+ (defined(__cpp_impl_trivially_relocatable) || \
+ (!defined(__clang__) && !defined(__APPLE__) && !defined(__NVCC__)))
template <class T>
struct is_trivially_relocatable
: std::integral_constant<bool, __is_trivially_relocatable(T)> {};
#else
// Otherwise we use a fallback that detects only those types we can feasibly
-// detect. Any time that has trivial move-construction and destruction
-// operations is by definition trivially relocatable.
+// detect. Any type that is trivially copyable is by definition trivially
+// relocatable.
template <class T>
-struct is_trivially_relocatable
- : absl::conjunction<absl::is_trivially_move_constructible<T>,
- absl::is_trivially_destructible<T>> {};
+struct is_trivially_relocatable : std::is_trivially_copyable<T> {};
#endif
// absl::is_constant_evaluated()
diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc
index 8f926901..25f5abbc 100644
--- a/absl/meta/type_traits_test.cc
+++ b/absl/meta/type_traits_test.cc
@@ -362,8 +362,8 @@ TEST(TypeTraitsTest, TestIsFunction) {
EXPECT_TRUE(absl::is_function<void() noexcept>::value);
EXPECT_TRUE(absl::is_function<void(...) noexcept>::value);
- EXPECT_FALSE(absl::is_function<void(*)()>::value);
- EXPECT_FALSE(absl::is_function<void(&)()>::value);
+ EXPECT_FALSE(absl::is_function<void (*)()>::value);
+ EXPECT_FALSE(absl::is_function<void (&)()>::value);
EXPECT_FALSE(absl::is_function<int>::value);
EXPECT_FALSE(absl::is_function<Callable>::value);
}
@@ -382,8 +382,8 @@ TEST(TypeTraitsTest, TestRemoveCVRef) {
// Does not remove const in this case.
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int*>::type,
const int*>::value));
- EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int[2]>::type,
- int[2]>::value));
+ EXPECT_TRUE(
+ (std::is_same<typename absl::remove_cvref<int[2]>::type, int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&)[2]>::type,
int[2]>::value));
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&&)[2]>::type,
@@ -580,7 +580,7 @@ TEST(TypeTraitsTest, TestDecay) {
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[][1]);
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int());
- ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(float)); // NOLINT
+ ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(float)); // NOLINT
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(char, ...)); // NOLINT
}
@@ -664,8 +664,7 @@ TEST(TypeTraitsTest, TestResultOf) {
namespace adl_namespace {
-struct DeletedSwap {
-};
+struct DeletedSwap {};
void swap(DeletedSwap&, DeletedSwap&) = delete;
@@ -751,7 +750,7 @@ TEST(TriviallyRelocatable, PrimitiveTypes) {
// User-defined types can be trivially relocatable as long as they don't have a
// user-provided move constructor or destructor.
-TEST(TriviallyRelocatable, UserDefinedTriviallyReconstructible) {
+TEST(TriviallyRelocatable, UserDefinedTriviallyRelocatable) {
struct S {
int x;
int y;
@@ -780,6 +779,30 @@ TEST(TriviallyRelocatable, UserProvidedCopyConstructor) {
static_assert(!absl::is_trivially_relocatable<S>::value, "");
}
+// A user-provided copy assignment operator disqualifies a type from
+// being trivially relocatable.
+TEST(TriviallyRelocatable, UserProvidedCopyAssignment) {
+ struct S {
+ S(const S&) = default;
+ S& operator=(const S&) { // NOLINT(modernize-use-equals-default)
+ return *this;
+ }
+ };
+
+ static_assert(!absl::is_trivially_relocatable<S>::value, "");
+}
+
+// A user-provided move assignment operator disqualifies a type from
+// being trivially relocatable.
+TEST(TriviallyRelocatable, UserProvidedMoveAssignment) {
+ struct S {
+ S(S&&) = default;
+ S& operator=(S&&) { return *this; } // NOLINT(modernize-use-equals-default)
+ };
+
+ static_assert(!absl::is_trivially_relocatable<S>::value, "");
+}
+
// A user-provided destructor disqualifies a type from being trivially
// relocatable.
TEST(TriviallyRelocatable, UserProvidedDestructor) {
@@ -794,18 +817,19 @@ TEST(TriviallyRelocatable, UserProvidedDestructor) {
// __is_trivially_relocatable is used there again.
// TODO(b/324278148): remove the opt-out for Apple once
// __is_trivially_relocatable is fixed there.
-#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) && \
- ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
- !(defined(__clang__) && (defined(_WIN32) || defined(_WIN64))) && \
- !defined(__APPLE__)
+#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) && \
+ ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
+ (defined(__cpp_impl_trivially_relocatable) || \
+ (!defined(__clang__) && !defined(__APPLE__) && !defined(__NVCC__)))
// A type marked with the "trivial ABI" attribute is trivially relocatable even
-// if it has user-provided move/copy constructors and a user-provided
-// destructor.
-TEST(TrivallyRelocatable, TrivialAbi) {
+// if it has user-provided special members.
+TEST(TriviallyRelocatable, TrivialAbi) {
struct ABSL_ATTRIBUTE_TRIVIAL_ABI S {
S(S&&) {} // NOLINT(modernize-use-equals-default)
S(const S&) {} // NOLINT(modernize-use-equals-default)
- ~S() {} // NOLINT(modernize-use-equals-default)
+ void operator=(S&&) {}
+ void operator=(const S&) {}
+ ~S() {} // NOLINT(modernize-use-equals-default)
};
static_assert(absl::is_trivially_relocatable<S>::value, "");
@@ -824,7 +848,7 @@ constexpr int64_t NegateIfConstantEvaluated(int64_t i) {
#endif // ABSL_HAVE_CONSTANT_EVALUATED
-TEST(TrivallyRelocatable, is_constant_evaluated) {
+TEST(IsConstantEvaluated, is_constant_evaluated) {
#ifdef ABSL_HAVE_CONSTANT_EVALUATED
constexpr int64_t constant = NegateIfConstantEvaluated(42);
EXPECT_EQ(constant, -42);
@@ -840,5 +864,4 @@ TEST(TrivallyRelocatable, is_constant_evaluated) {
#endif // ABSL_HAVE_CONSTANT_EVALUATED
}
-
} // namespace