Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 23 additions & 13 deletions absl/meta/type_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -471,12 +471,17 @@ using swap_internal::StdSwapIsUnconstrained;
// 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.
//
// 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<int> is trivially
// relocatable on every known STL implementation, but
// absl::is_trivially_relocatable<std::vector<int>> remains false.)
//
// Example:
//
Expand Down Expand Up @@ -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 <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>> {};
: std::is_trivially_copyable<T> {};
#endif

// absl::is_constant_evaluated()
Expand Down
37 changes: 30 additions & 7 deletions absl/meta/type_traits_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -780,6 +780,28 @@ 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&) { return *this; } // NOLINT(modernize-use-equals-default)
};

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) {
Expand All @@ -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)
};

Expand All @@ -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);
Expand Down