diff --git a/absl/meta/type_traits.h b/absl/meta/type_traits.h index 438bfc76613..88633b25d30 100644 --- a/absl/meta/type_traits.h +++ b/absl/meta/type_traits.h @@ -152,8 +152,8 @@ template struct disjunction : std::false_type {}; template -struct disjunction : - std::conditional>::type {}; +struct disjunction + : std::conditional>::type {}; template struct disjunction : T {}; @@ -315,22 +315,23 @@ using common_type_t = typename std::common_type::type; template using underlying_type_t = typename std::underlying_type::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 struct result_of; -template +template +struct result_of; +template struct result_of : std::invoke_result {}; #else -template using result_of = std::result_of; +template +using result_of = std::result_of; #endif } // namespace type_traits_internal -template +template using result_of_t = typename type_traits_internal::result_of::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 // // 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 is trivially relocatable on every known STL +// implementation, but absl::is_trivially_relocatable> 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 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> {}; +struct is_trivially_relocatable : 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..25f5abbce70 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::value); EXPECT_TRUE(absl::is_function::value); - EXPECT_FALSE(absl::is_function::value); - EXPECT_FALSE(absl::is_function::value); + EXPECT_FALSE(absl::is_function::value); + EXPECT_FALSE(absl::is_function::value); EXPECT_FALSE(absl::is_function::value); EXPECT_FALSE(absl::is_function::value); } @@ -382,8 +382,8 @@ TEST(TypeTraitsTest, TestRemoveCVRef) { // Does not remove const in this case. EXPECT_TRUE((std::is_same::type, const int*>::value)); - EXPECT_TRUE((std::is_same::type, - int[2]>::value)); + EXPECT_TRUE( + (std::is_same::type, int[2]>::value)); EXPECT_TRUE((std::is_same::type, int[2]>::value)); EXPECT_TRUE((std::is_same::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::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::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) { @@ -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::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