Skip to content

Commit 14b8a4e

Browse files
Quuxplusonecopybara-github
authored andcommitted
PR #1625: absl::is_trivially_relocatable now respects assignment operators
Imported from GitHub PR #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 d943abd into 34604d5 Merging this change closes #1625 COPYBARA_INTEGRATE_REVIEW=#1625 from Quuxplusone:trivially-relocatable d943abd PiperOrigin-RevId: 607977323 Change-Id: I6436a60326c6d1064bdd71ec2e15b86b7a29efd4
1 parent 8a3caf7 commit 14b8a4e

File tree

2 files changed

+70
-39
lines changed

2 files changed

+70
-39
lines changed

absl/meta/type_traits.h

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ template <typename... Ts>
152152
struct disjunction : std::false_type {};
153153

154154
template <typename T, typename... Ts>
155-
struct disjunction<T, Ts...> :
156-
std::conditional<T::value, T, disjunction<Ts...>>::type {};
155+
struct disjunction<T, Ts...>
156+
: std::conditional<T::value, T, disjunction<Ts...>>::type {};
157157

158158
template <typename T>
159159
struct disjunction<T> : T {};
@@ -315,22 +315,23 @@ using common_type_t = typename std::common_type<T...>::type;
315315
template <typename T>
316316
using underlying_type_t = typename std::underlying_type<T>::type;
317317

318-
319318
namespace type_traits_internal {
320319

321320
#if (defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703L) || \
322321
(defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
323322
// std::result_of is deprecated (C++17) or removed (C++20)
324-
template<typename> struct result_of;
325-
template<typename F, typename... Args>
323+
template <typename>
324+
struct result_of;
325+
template <typename F, typename... Args>
326326
struct result_of<F(Args...)> : std::invoke_result<F, Args...> {};
327327
#else
328-
template<typename F> using result_of = std::result_of<F>;
328+
template <typename F>
329+
using result_of = std::result_of<F>;
329330
#endif
330331

331332
} // namespace type_traits_internal
332333

333-
template<typename F>
334+
template <typename F>
334335
using result_of_t = typename type_traits_internal::result_of<F>::type;
335336

336337
namespace type_traits_internal {
@@ -463,20 +464,23 @@ namespace type_traits_internal {
463464
// Make the swap-related traits/function accessible from this namespace.
464465
using swap_internal::IsNothrowSwappable;
465466
using swap_internal::IsSwappable;
466-
using swap_internal::Swap;
467467
using swap_internal::StdSwapIsUnconstrained;
468+
using swap_internal::Swap;
468469

469470
} // namespace type_traits_internal
470471

471472
// absl::is_trivially_relocatable<T>
472473
//
473474
// Detects whether a type is known to be "trivially relocatable" -- meaning it
474-
// can be relocated without invoking the constructor/destructor, using a form of
475-
// move elision.
475+
// can be relocated from one place to another as if by memcpy/memmove.
476+
// This implies that its object representation doesn't depend on its address,
477+
// and also none of its special member functions do anything strange.
476478
//
477-
// This trait is conservative, for backwards compatibility. If it's true then
478-
// the type is definitely trivially relocatable, but if it's false then the type
479-
// may or may not be.
479+
// This trait is conservative. If it's true then the type is definitely
480+
// trivially relocatable, but if it's false then the type may or may not be. For
481+
// example, std::vector<int> is trivially relocatable on every known STL
482+
// implementation, but absl::is_trivially_relocatable<std::vector<int>> remains
483+
// false.
480484
//
481485
// Example:
482486
//
@@ -509,22 +513,26 @@ using swap_internal::StdSwapIsUnconstrained;
509513
// TODO(b/324278148): If all versions we use have the bug fixed, then
510514
// remove the condition.
511515
//
516+
// Clang on all platforms fails to detect that a type with a user-provided
517+
// move-assignment operator is not trivially relocatable. So in fact we
518+
// opt out of Clang altogether, for now.
519+
//
520+
// TODO(b/325479096): Remove the opt-out once Clang's behavior is fixed.
521+
//
512522
// According to https://github.com/abseil/abseil-cpp/issues/1479, this does not
513523
// work with NVCC either.
514-
#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
515-
!(defined(__clang__) && (defined(_WIN32) || defined(_WIN64))) && \
516-
!(defined(__APPLE__)) && !defined(__NVCC__)
524+
#if ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
525+
(defined(__cpp_impl_trivially_relocatable) || \
526+
(!defined(__clang__) && !defined(__APPLE__) && !defined(__NVCC__)))
517527
template <class T>
518528
struct is_trivially_relocatable
519529
: std::integral_constant<bool, __is_trivially_relocatable(T)> {};
520530
#else
521531
// Otherwise we use a fallback that detects only those types we can feasibly
522-
// detect. Any time that has trivial move-construction and destruction
523-
// operations is by definition trivially relocatable.
532+
// detect. Any type that is trivially copyable is by definition trivially
533+
// relocatable.
524534
template <class T>
525-
struct is_trivially_relocatable
526-
: absl::conjunction<absl::is_trivially_move_constructible<T>,
527-
absl::is_trivially_destructible<T>> {};
535+
struct is_trivially_relocatable : std::is_trivially_copyable<T> {};
528536
#endif
529537

530538
// absl::is_constant_evaluated()

absl/meta/type_traits_test.cc

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,8 @@ TEST(TypeTraitsTest, TestIsFunction) {
362362
EXPECT_TRUE(absl::is_function<void() noexcept>::value);
363363
EXPECT_TRUE(absl::is_function<void(...) noexcept>::value);
364364

365-
EXPECT_FALSE(absl::is_function<void(*)()>::value);
366-
EXPECT_FALSE(absl::is_function<void(&)()>::value);
365+
EXPECT_FALSE(absl::is_function<void (*)()>::value);
366+
EXPECT_FALSE(absl::is_function<void (&)()>::value);
367367
EXPECT_FALSE(absl::is_function<int>::value);
368368
EXPECT_FALSE(absl::is_function<Callable>::value);
369369
}
@@ -382,8 +382,8 @@ TEST(TypeTraitsTest, TestRemoveCVRef) {
382382
// Does not remove const in this case.
383383
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<const int*>::type,
384384
const int*>::value));
385-
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int[2]>::type,
386-
int[2]>::value));
385+
EXPECT_TRUE(
386+
(std::is_same<typename absl::remove_cvref<int[2]>::type, int[2]>::value));
387387
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&)[2]>::type,
388388
int[2]>::value));
389389
EXPECT_TRUE((std::is_same<typename absl::remove_cvref<int(&&)[2]>::type,
@@ -580,7 +580,7 @@ TEST(TypeTraitsTest, TestDecay) {
580580
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int[][1]);
581581

582582
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int());
583-
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(float)); // NOLINT
583+
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(float)); // NOLINT
584584
ABSL_INTERNAL_EXPECT_ALIAS_EQUIVALENCE(decay, int(char, ...)); // NOLINT
585585
}
586586

@@ -664,8 +664,7 @@ TEST(TypeTraitsTest, TestResultOf) {
664664

665665
namespace adl_namespace {
666666

667-
struct DeletedSwap {
668-
};
667+
struct DeletedSwap {};
669668

670669
void swap(DeletedSwap&, DeletedSwap&) = delete;
671670

@@ -751,7 +750,7 @@ TEST(TriviallyRelocatable, PrimitiveTypes) {
751750

752751
// User-defined types can be trivially relocatable as long as they don't have a
753752
// user-provided move constructor or destructor.
754-
TEST(TriviallyRelocatable, UserDefinedTriviallyReconstructible) {
753+
TEST(TriviallyRelocatable, UserDefinedTriviallyRelocatable) {
755754
struct S {
756755
int x;
757756
int y;
@@ -780,6 +779,30 @@ TEST(TriviallyRelocatable, UserProvidedCopyConstructor) {
780779
static_assert(!absl::is_trivially_relocatable<S>::value, "");
781780
}
782781

782+
// A user-provided copy assignment operator disqualifies a type from
783+
// being trivially relocatable.
784+
TEST(TriviallyRelocatable, UserProvidedCopyAssignment) {
785+
struct S {
786+
S(const S&) = default;
787+
S& operator=(const S&) { // NOLINT(modernize-use-equals-default)
788+
return *this;
789+
}
790+
};
791+
792+
static_assert(!absl::is_trivially_relocatable<S>::value, "");
793+
}
794+
795+
// A user-provided move assignment operator disqualifies a type from
796+
// being trivially relocatable.
797+
TEST(TriviallyRelocatable, UserProvidedMoveAssignment) {
798+
struct S {
799+
S(S&&) = default;
800+
S& operator=(S&&) { return *this; } // NOLINT(modernize-use-equals-default)
801+
};
802+
803+
static_assert(!absl::is_trivially_relocatable<S>::value, "");
804+
}
805+
783806
// A user-provided destructor disqualifies a type from being trivially
784807
// relocatable.
785808
TEST(TriviallyRelocatable, UserProvidedDestructor) {
@@ -794,18 +817,19 @@ TEST(TriviallyRelocatable, UserProvidedDestructor) {
794817
// __is_trivially_relocatable is used there again.
795818
// TODO(b/324278148): remove the opt-out for Apple once
796819
// __is_trivially_relocatable is fixed there.
797-
#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) && \
798-
ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
799-
!(defined(__clang__) && (defined(_WIN32) || defined(_WIN64))) && \
800-
!defined(__APPLE__)
820+
#if defined(ABSL_HAVE_ATTRIBUTE_TRIVIAL_ABI) && \
821+
ABSL_HAVE_BUILTIN(__is_trivially_relocatable) && \
822+
(defined(__cpp_impl_trivially_relocatable) || \
823+
(!defined(__clang__) && !defined(__APPLE__) && !defined(__NVCC__)))
801824
// A type marked with the "trivial ABI" attribute is trivially relocatable even
802-
// if it has user-provided move/copy constructors and a user-provided
803-
// destructor.
804-
TEST(TrivallyRelocatable, TrivialAbi) {
825+
// if it has user-provided special members.
826+
TEST(TriviallyRelocatable, TrivialAbi) {
805827
struct ABSL_ATTRIBUTE_TRIVIAL_ABI S {
806828
S(S&&) {} // NOLINT(modernize-use-equals-default)
807829
S(const S&) {} // NOLINT(modernize-use-equals-default)
808-
~S() {} // NOLINT(modernize-use-equals-default)
830+
void operator=(S&&) {}
831+
void operator=(const S&) {}
832+
~S() {} // NOLINT(modernize-use-equals-default)
809833
};
810834

811835
static_assert(absl::is_trivially_relocatable<S>::value, "");
@@ -824,7 +848,7 @@ constexpr int64_t NegateIfConstantEvaluated(int64_t i) {
824848

825849
#endif // ABSL_HAVE_CONSTANT_EVALUATED
826850

827-
TEST(TrivallyRelocatable, is_constant_evaluated) {
851+
TEST(IsConstantEvaluated, is_constant_evaluated) {
828852
#ifdef ABSL_HAVE_CONSTANT_EVALUATED
829853
constexpr int64_t constant = NegateIfConstantEvaluated(42);
830854
EXPECT_EQ(constant, -42);
@@ -840,5 +864,4 @@ TEST(TrivallyRelocatable, is_constant_evaluated) {
840864
#endif // ABSL_HAVE_CONSTANT_EVALUATED
841865
}
842866

843-
844867
} // namespace

0 commit comments

Comments
 (0)