[libcxx-commits] [libcxx] [libc++] Mark more types as trivially relocatable (PR #89724)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Tue Apr 23 02:02:53 PDT 2024
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/89724
None
>From aa3d780cf623f17ac31fd4f5890e7e846b2507dd Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Mon, 15 Jan 2024 16:28:42 +0100
Subject: [PATCH] [libc++] Mark more types as trivially relocatable
---
libcxx/include/__exception/exception_ptr.h | 3 +
libcxx/include/__expected/expected.h | 6 +
libcxx/include/__locale | 3 +
libcxx/include/__memory/shared_ptr.h | 8 ++
libcxx/include/__split_buffer | 9 ++
libcxx/include/__utility/pair.h | 6 +
libcxx/include/array | 2 +
libcxx/include/deque | 10 ++
libcxx/include/optional | 2 +
libcxx/include/tuple | 2 +
libcxx/include/variant | 3 +
libcxx/include/vector | 9 ++
.../is_trivially_relocatable.compile.pass.cpp | 103 ++++++++++++++++++
13 files changed, 166 insertions(+)
diff --git a/libcxx/include/__exception/exception_ptr.h b/libcxx/include/__exception/exception_ptr.h
index c9027de9238cdd..f7026c437e883f 100644
--- a/libcxx/include/__exception/exception_ptr.h
+++ b/libcxx/include/__exception/exception_ptr.h
@@ -64,6 +64,9 @@ class _LIBCPP_EXPORTED_FROM_ABI exception_ptr {
friend _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep) _NOEXCEPT;
public:
+ // exception_ptr is basically a COW string.
+ using __trivially_relocatable = exception_ptr;
+
_LIBCPP_HIDE_FROM_ABI exception_ptr() _NOEXCEPT : __ptr_() {}
_LIBCPP_HIDE_FROM_ABI exception_ptr(nullptr_t) _NOEXCEPT : __ptr_() {}
diff --git a/libcxx/include/__expected/expected.h b/libcxx/include/__expected/expected.h
index d7adaac7567b2f..0f994e297a877a 100644
--- a/libcxx/include/__expected/expected.h
+++ b/libcxx/include/__expected/expected.h
@@ -31,6 +31,7 @@
#include <__type_traits/is_swappable.h>
#include <__type_traits/is_trivially_constructible.h>
#include <__type_traits/is_trivially_destructible.h>
+#include <__type_traits/is_trivially_relocatable.h>
#include <__type_traits/is_void.h>
#include <__type_traits/lazy.h>
#include <__type_traits/negation.h>
@@ -463,6 +464,11 @@ class expected : private __expected_base<_Tp, _Err> {
using error_type = _Err;
using unexpected_type = unexpected<_Err>;
+ using __trivially_relocatable =
+ __conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value && __libcpp_is_trivially_relocatable<_Err>::value,
+ expected,
+ void>;
+
template <class _Up>
using rebind = expected<_Up, error_type>;
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index 36ac099d650e4c..fcae7c5efbe60f 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -49,6 +49,9 @@ _LIBCPP_HIDE_FROM_ABI const _Facet& use_facet(const locale&);
class _LIBCPP_EXPORTED_FROM_ABI locale {
public:
+ // locale is essentially a shared_ptr that doesn't support weak_ptrs and never got a move constructor.
+ using __trivially_relocatable = locale;
+
// types:
class _LIBCPP_EXPORTED_FROM_ABI facet;
class _LIBCPP_EXPORTED_FROM_ABI id;
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 992b1ba43f100d..fe87e0d04d8bb8 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -419,6 +419,10 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr {
typedef _Tp element_type;
#endif
+ // A shared_ptr contains only two raw pointers which point to the heap and move constructing already doesn't require
+ // any bookkeeping, so it's always trivially relocatable.
+ using __trivially_relocatable = shared_ptr;
+
private:
element_type* __ptr_;
__shared_weak_count* __cntrl_;
@@ -1301,6 +1305,10 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS weak_ptr {
typedef _Tp element_type;
#endif
+ // A weak_ptr contains only two raw pointers which point to the heap and move constructing already doesn't require
+ // any bookkeeping, so it's always trivially relocatable.
+ using __trivially_relocatable = weak_ptr;
+
private:
element_type* __ptr_;
__shared_weak_count* __cntrl_;
diff --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index c68349e0979c93..90f810e6a42c67 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -64,6 +64,15 @@ public:
using iterator = pointer;
using const_iterator = const_pointer;
+ // A __split_buffer containers the following members which may be trivially relocatable:
+ // - pointer: may be trivially relocatable, so it's checked
+ // - allocator_type: may be trivially relocatable, so it's checked
+ // __split_buffer doesn't have any self-references, so it's trivially relocatable if its members are.
+ using __trivially_relocatable = __conditional_t<
+ __libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
+ __split_buffer,
+ void>;
+
pointer __first_;
pointer __begin_;
pointer __end_;
diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h
index e05250ba05717f..c0b4215dbb5f05 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -34,6 +34,7 @@
#include <__type_traits/is_nothrow_constructible.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_swappable.h>
+#include <__type_traits/is_trivially_relocatable.h>
#include <__type_traits/nat.h>
#include <__type_traits/remove_cvref.h>
#include <__type_traits/unwrap_ref.h>
@@ -71,6 +72,11 @@ struct _LIBCPP_TEMPLATE_VIS pair
_T1 first;
_T2 second;
+ using __trivially_relocatable =
+ __conditional_t<__libcpp_is_trivially_relocatable<_T1>::value && __libcpp_is_trivially_relocatable<_T2>::value,
+ pair,
+ void>;
+
_LIBCPP_HIDE_FROM_ABI pair(pair const&) = default;
_LIBCPP_HIDE_FROM_ABI pair(pair&&) = default;
diff --git a/libcxx/include/array b/libcxx/include/array
index 977f913c3e748a..d16b497c75d163 100644
--- a/libcxx/include/array
+++ b/libcxx/include/array
@@ -166,6 +166,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp, size_t _Size>
struct _LIBCPP_TEMPLATE_VIS array {
+ using __trivially_relocatable = __conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, array, void>;
+
// types:
using __self = array;
using value_type = _Tp;
diff --git a/libcxx/include/deque b/libcxx/include/deque
index d42669dd6dc0e1..a85bbfeabb4753 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -478,6 +478,16 @@ public:
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
+ // A deque contains the following members which may be trivially relocatable:
+ // - __map: is a `__split_buffer`, see `__split_buffer` for more information on when it is trivially relocatable
+ // - size_type: is always trivially relocatable, since it is required to be an integral type
+ // - allocator_type: may not be trivially relocatable, so it's checked
+ // None of these are referencing the `deque` itself, so if all of them are trivially relocatable, `deque` is too.
+ using __trivially_relocatable = __conditional_t<
+ __libcpp_is_trivially_relocatable<__map>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
+ deque,
+ void>;
+
static_assert(is_same<allocator_type, __rebind_alloc<__alloc_traits, value_type> >::value,
"[allocator.requirements] states that rebinding an allocator to the same type should result in the "
"original allocator");
diff --git a/libcxx/include/optional b/libcxx/include/optional
index a16e48502e2509..f1a441efa98948 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -581,6 +581,8 @@ class _LIBCPP_DECLSPEC_EMPTY_BASES optional
public:
using value_type = _Tp;
+ using __trivially_relocatable = conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value, optional, void>;
+
private:
// Disable the reference extension using this static assert.
static_assert(!is_same_v<__remove_cvref_t<value_type>, in_place_t>,
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index c7fc5509a0f808..c196617525b75c 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -538,6 +538,8 @@ class _LIBCPP_TEMPLATE_VIS tuple {
get(const tuple<_Up...>&&) _NOEXCEPT;
public:
+ using __trivially_relocatable = __conditional_t<_And<__libcpp_is_trivially_relocatable<_Tp>...>::value, tuple, void>;
+
// [tuple.cnstr]
// tuple() constructors (including allocator_arg_t variants)
diff --git a/libcxx/include/variant b/libcxx/include/variant
index 858a49b980bd9a..ffc66832cfe8cf 100644
--- a/libcxx/include/variant
+++ b/libcxx/include/variant
@@ -1151,6 +1151,9 @@ class _LIBCPP_TEMPLATE_VIS _LIBCPP_DECLSPEC_EMPTY_BASES variant
using __first_type = variant_alternative_t<0, variant>;
public:
+ using __trivially_relocatable =
+ conditional_t<(__libcpp_is_trivially_relocatable<_Types>::value && ...), variant, void>;
+
template <bool _Dummy = true,
enable_if_t<__dependent_type<is_default_constructible<__first_type>, _Dummy>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI constexpr variant() noexcept(is_nothrow_default_constructible_v<__first_type>)
diff --git a/libcxx/include/vector b/libcxx/include/vector
index 7303d7ec0b24a3..4e00f3c6cdecd6 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -407,6 +407,15 @@ public:
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
+ // A vector containers the following members which may be trivially relocatable:
+ // - pointer: may be trivially relocatable, so it's checked
+ // - allocator_type: may be trivially relocatable, so it's checked
+ // vector doesn't contain any self-references, so it's trivially relocatable if its members are.
+ using __trivially_relocatable = __conditional_t<
+ __libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
+ vector,
+ void>;
+
static_assert((is_same<typename allocator_type::value_type, value_type>::value),
"Allocator::value_type must be same type as value_type");
diff --git a/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp b/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
index 389816bb23aa90..fe98878430e2c5 100644
--- a/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
+++ b/libcxx/test/libcxx/type_traits/is_trivially_relocatable.compile.pass.cpp
@@ -7,8 +7,16 @@
//===----------------------------------------------------------------------===//
#include <__type_traits/is_trivially_relocatable.h>
+#include <array>
+#include <deque>
+#include <exception>
+#include <expected>
+#include <locale>
#include <memory>
+#include <optional>
#include <string>
+#include <variant>
+#include <vector>
#include "constexpr_char_traits.h"
#include "test_allocator.h"
@@ -44,9 +52,27 @@ static_assert(std::__libcpp_is_trivially_relocatable<MoveOnlyTriviallyCopyable>:
#else
static_assert(!std::__libcpp_is_trivially_relocatable<MoveOnlyTriviallyCopyable>::value, "");
#endif
+
+// library-internal types
+// ----------------------
+
+// __split_buffer
+static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<int> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::__split_buffer<NotTriviallyCopyable> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::__split_buffer<int, test_allocator<int> > >::value, "");
+
// standard library types
// ----------------------
+// array
+static_assert(std::__libcpp_is_trivially_relocatable<std::array<int, 0> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::array<NotTriviallyCopyable, 0> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<int>, 0> >::value, "");
+
+static_assert(std::__libcpp_is_trivially_relocatable<std::array<int, 1> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::array<NotTriviallyCopyable, 1> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::array<std::unique_ptr<int>, 1> >::value, "");
+
// basic_string
struct MyChar {
char c;
@@ -79,6 +105,62 @@ static_assert(
std::basic_string<MyChar, NotTriviallyRelocatableCharTraits<MyChar>, test_allocator<MyChar> > >::value,
"");
+// deque
+static_assert(std::__libcpp_is_trivially_relocatable<std::deque<int> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::deque<NotTriviallyCopyable> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::deque<int, test_allocator<int> > >::value, "");
+
+// exception_ptr
+static_assert(std::__libcpp_is_trivially_relocatable<std::exception_ptr>::value, "");
+
+// expected
+#if TEST_STD_VER >= 23
+static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, int> >::value);
+static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, int>>::value);
+static_assert(std::__libcpp_is_trivially_relocatable<std::expected<int, std::unique_ptr<int>>>::value);
+static_assert(std::__libcpp_is_trivially_relocatable<std::expected<std::unique_ptr<int>, std::unique_ptr<int>>>::value);
+
+static_assert(!std::__libcpp_is_trivially_relocatable<std::expected<int, NotTriviallyCopyable>>::value);
+static_assert(!std::__libcpp_is_trivially_relocatable<std::expected<NotTriviallyCopyable, int>>::value);
+static_assert(
+ !std::__libcpp_is_trivially_relocatable<std::expected<NotTriviallyCopyable, NotTriviallyCopyable>>::value);
+#endif
+
+// locale
+static_assert(std::__libcpp_is_trivially_relocatable<std::locale>::value, "");
+
+// optional
+static_assert(std::__libcpp_is_trivially_relocatable<std::optional<int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::optional<NotTriviallyCopyable> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::optional<std::unique_ptr<int> > >::value, "");
+
+// pair
+static_assert(std::__libcpp_is_trivially_relocatable<std::pair<int, int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<NotTriviallyCopyable, int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<int, NotTriviallyCopyable> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::pair<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
+ "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::pair<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
+ "");
+
+// shared_ptr
+static_assert(std::__libcpp_is_trivially_relocatable<std::shared_ptr<NotTriviallyCopyable> >::value, "");
+
+// tuple
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<> >::value, "");
+
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int> > >::value, "");
+
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<int, int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable, int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<int, NotTriviallyCopyable> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::tuple<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
+ "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::tuple<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
+ "");
+
// unique_ptr
struct NotTriviallyRelocatableDeleter {
NotTriviallyRelocatableDeleter(const NotTriviallyRelocatableDeleter&);
@@ -112,4 +194,25 @@ static_assert(!std::__libcpp_is_trivially_relocatable<std::unique_ptr<int, NotTr
static_assert(!std::__libcpp_is_trivially_relocatable<std::unique_ptr<int[], NotTriviallyRelocatablePointer> >::value,
"");
+// variant
+static_assert(std::__libcpp_is_trivially_relocatable<std::variant<int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int> > >::value, "");
+
+static_assert(std::__libcpp_is_trivially_relocatable<std::variant<int, int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable, int> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<int, NotTriviallyCopyable> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::variant<NotTriviallyCopyable, NotTriviallyCopyable> >::value,
+ "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::variant<std::unique_ptr<int>, std::unique_ptr<int> > >::value,
+ "");
+
+// vector
+static_assert(std::__libcpp_is_trivially_relocatable<std::vector<int> >::value, "");
+static_assert(std::__libcpp_is_trivially_relocatable<std::vector<NotTriviallyCopyable> >::value, "");
+static_assert(!std::__libcpp_is_trivially_relocatable<std::vector<int, test_allocator<int> > >::value, "");
+
+// weak_ptr
+static_assert(std::__libcpp_is_trivially_relocatable<std::weak_ptr<NotTriviallyCopyable> >::value, "");
+
// TODO: Mark all the trivially relocatable STL types as such
More information about the libcxx-commits
mailing list