diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index a456ae4f299..92ad1e559c2 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -471,12 +471,17 @@ using swap_internal::StdSwapIsUnconstrained; // absl::is_trivially_relocatable // // 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. -// -// 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. +// 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. If it's true then the type is definitely +// trivially relocatable, but there are many types which are "Platonically" +// trivially relocatable but for which the type trait returns false because +// it can't introspect into the special members and see that they're not +// doing anything strange. (For example, std::vector is trivially +// relocatable on every known STL implementation, but +// absl::is_trivially_relocatable> remains false.) // // Example: // @@ -509,22 +514,27 @@ 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: 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 struct is_trivially_relocatable : std::integral_constant {}; #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 struct is_trivially_relocatable - : absl::conjunction, - absl::is_trivially_destructible> {}; + : std::is_trivially_copyable {}; #endif // absl::is_constant_evaluated() diff --git a/absl/meta/type_traits_test.cc b/absl/meta/type_traits_test.cc index 8f926901244..13a27985e93 100644 --- a/absl/meta/type_traits_test.cc +++ b/absl/meta/type_traits_test.cc @@ -751,7 +751,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 +780,28 @@ TEST(TriviallyRelocatable, UserProvidedCopyConstructor) { static_assert(!absl::is_trivially_relocatable::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&) { return *this; } // NOLINT(modernize-use-equals-default) + }; + + static_assert(!absl::is_trivially_relocatable::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::value, ""); +} + // A user-provided destructor disqualifies a type from being trivially // relocatable. TEST(TriviallyRelocatable, UserProvidedDestructor) { @@ -796,15 +818,16 @@ TEST(TriviallyRelocatable, UserProvidedDestructor) { // __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__) + (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) + void operator=(S&&) {} + void operator=(const S&) {} ~S() {} // NOLINT(modernize-use-equals-default) }; @@ -824,7 +847,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);