Skip to content

Commit 0115849

Browse files
committed
[libc++] Add the __is_replaceable type trait
That type trait represents whether move-assigning an object is equivalent to destroying it and then move-constructing a new one from the same argument. This will be useful in a few places where we may want to destroy + construct instead of doing an assignment, in particular with relocation.
1 parent 741987f commit 0115849

File tree

18 files changed

+385
-2
lines changed

18 files changed

+385
-2
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -813,6 +813,7 @@ set(files
813813
__type_traits/is_reference.h
814814
__type_traits/is_reference_wrapper.h
815815
__type_traits/is_referenceable.h
816+
__type_traits/is_replaceable.h
816817
__type_traits/is_same.h
817818
__type_traits/is_scalar.h
818819
__type_traits/is_signed.h

libcxx/include/__exception/exception_ptr.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,11 @@ class _LIBCPP_EXPORTED_FROM_ABI exception_ptr {
6666
friend _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep) _NOEXCEPT;
6767

6868
public:
69-
// exception_ptr is basically a COW string.
69+
// exception_ptr is basically a COW string so it is trivially relocatable.
70+
// However, it's not replaceable because destroying and move-constructing could cause
71+
// the underlying refcount to hit 0 if we're self-assigning.
7072
using __trivially_relocatable = exception_ptr;
73+
using __replaceable = void;
7174

7275
_LIBCPP_HIDE_FROM_ABI exception_ptr() _NOEXCEPT : __ptr_() {}
7376
_LIBCPP_HIDE_FROM_ABI exception_ptr(nullptr_t) _NOEXCEPT : __ptr_() {}

libcxx/include/__expected/expected.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <__type_traits/is_nothrow_assignable.h>
3030
#include <__type_traits/is_nothrow_constructible.h>
3131
#include <__type_traits/is_reference.h>
32+
#include <__type_traits/is_replaceable.h>
3233
#include <__type_traits/is_same.h>
3334
#include <__type_traits/is_swappable.h>
3435
#include <__type_traits/is_trivially_constructible.h>
@@ -470,6 +471,7 @@ class expected : private __expected_base<_Tp, _Err> {
470471
__conditional_t<__libcpp_is_trivially_relocatable<_Tp>::value && __libcpp_is_trivially_relocatable<_Err>::value,
471472
expected,
472473
void>;
474+
using __replaceable = __conditional_t<__is_replaceable<_Tp>::value && __is_replaceable<_Err>::value, expected, void>;
473475

474476
template <class _Up>
475477
using rebind = expected<_Up, error_type>;

libcxx/include/__locale

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@ _LIBCPP_HIDE_FROM_ABI const _Facet& use_facet(const locale&);
4949

5050
class _LIBCPP_EXPORTED_FROM_ABI locale {
5151
public:
52-
// locale is essentially a shared_ptr that doesn't support weak_ptrs and never got a move constructor.
52+
// locale is essentially a shared_ptr that doesn't support weak_ptrs and never got a move constructor,
53+
// so it is trivially relocatable. However, it is not replaceable because self-assignment must prevent
54+
// the refcount from hitting 0.
5355
using __trivially_relocatable = locale;
56+
using __replaceable = void;
5457

5558
// types:
5659
class _LIBCPP_EXPORTED_FROM_ABI facet;

libcxx/include/__memory/shared_ptr.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,11 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr {
315315

316316
// A shared_ptr contains only two raw pointers which point to the heap and move constructing already doesn't require
317317
// any bookkeeping, so it's always trivially relocatable.
318+
//
319+
// However, it's not replaceable because of self-assignment, which must prevent the refcount from
320+
// hitting 0.
318321
using __trivially_relocatable = shared_ptr;
322+
using __replaceable = void;
319323

320324
private:
321325
element_type* __ptr_;
@@ -1210,7 +1214,10 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS weak_ptr {
12101214

12111215
// A weak_ptr contains only two raw pointers which point to the heap and move constructing already doesn't require
12121216
// any bookkeeping, so it's always trivially relocatable.
1217+
//
1218+
// However, it's not replaceable because we must preserve a non-zero refcount through self-assignment.
12131219
using __trivially_relocatable = weak_ptr;
1220+
using __replaceable = void;
12141221

12151222
private:
12161223
element_type* __ptr_;

libcxx/include/__memory/unique_ptr.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include <__type_traits/is_function.h>
4040
#include <__type_traits/is_pointer.h>
4141
#include <__type_traits/is_reference.h>
42+
#include <__type_traits/is_replaceable.h>
4243
#include <__type_traits/is_same.h>
4344
#include <__type_traits/is_swappable.h>
4445
#include <__type_traits/is_trivially_relocatable.h>
@@ -157,6 +158,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr {
157158
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<deleter_type>::value,
158159
unique_ptr,
159160
void>;
161+
using __replaceable =
162+
__conditional_t<__is_replaceable<pointer>::value && __is_replaceable<deleter_type>::value, unique_ptr, void>;
160163

161164
private:
162165
_LIBCPP_COMPRESSED_PAIR(pointer, __ptr_, deleter_type, __deleter_);
@@ -423,6 +426,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
423426
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<deleter_type>::value,
424427
unique_ptr,
425428
void>;
429+
using __replaceable =
430+
__conditional_t<__is_replaceable<pointer>::value && __is_replaceable<deleter_type>::value, unique_ptr, void>;
426431

427432
private:
428433
template <class _Up, class _OtherDeleter>

libcxx/include/__split_buffer

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <__type_traits/integral_constant.h>
3131
#include <__type_traits/is_nothrow_assignable.h>
3232
#include <__type_traits/is_nothrow_constructible.h>
33+
#include <__type_traits/is_replaceable.h>
3334
#include <__type_traits/is_swappable.h>
3435
#include <__type_traits/is_trivially_destructible.h>
3536
#include <__type_traits/is_trivially_relocatable.h>
@@ -74,6 +75,10 @@ public:
7475
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
7576
__split_buffer,
7677
void>;
78+
using __replaceable =
79+
__conditional_t<__is_replaceable<pointer>::value && __container_allocator_is_replaceable<__alloc_traits>::value,
80+
__split_buffer,
81+
void>;
7782

7883
pointer __first_;
7984
pointer __begin_;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H
10+
#define _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H
11+
12+
#include <__config>
13+
#include <__type_traits/enable_if.h>
14+
#include <__type_traits/integral_constant.h>
15+
#include <__type_traits/is_same.h>
16+
#include <__type_traits/is_trivially_copyable.h>
17+
18+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
19+
# pragma GCC system_header
20+
#endif
21+
22+
_LIBCPP_BEGIN_NAMESPACE_STD
23+
24+
// A type is replaceable if `x = std::move(y)` is equivalent to:
25+
//
26+
// std::destroy_at(&x)
27+
// std::construct_at(&x, std::move(y))
28+
//
29+
// This allows turning a move-assignment into a sequence of destroy + move-construct, which
30+
// is often more efficient. This is especially relevant when the move-construct is in fact
31+
// part of a trivial relocation from somewhere else, in which case there is a huge win.
32+
//
33+
// Note that this requires language support in order to be really effective, but we
34+
// currently emulate the base template with something very conservative.
35+
template <class _Tp, class = void>
36+
struct __is_replaceable : is_trivially_copyable<_Tp> {};
37+
38+
template <class _Tp>
39+
struct __is_replaceable<_Tp, __enable_if_t<is_same<_Tp, typename _Tp::__replaceable>::value> > : true_type {};
40+
41+
// Determines whether an allocator member of a container is replaceable.
42+
//
43+
// We take into account whether the allocator is propagated on assignments. If the allocator
44+
// always compares equal, then it doesn't matter whether we propagate it or not on assignments,
45+
// the result will be the same and we can just as much move-construct it instead.
46+
//
47+
// If the allocator does not always compare equal, we check whether it propagates on assignment
48+
// and it is replaceable.
49+
template <class _AllocatorTraits>
50+
struct __container_allocator_is_replaceable
51+
: integral_constant<bool,
52+
_AllocatorTraits::is_always_equal::value ||
53+
(_AllocatorTraits::propagate_on_container_move_assignment::value &&
54+
_AllocatorTraits::propagate_on_container_copy_assignment::value &&
55+
__is_replaceable<typename _AllocatorTraits::allocator_type>::value)> {};
56+
57+
_LIBCPP_END_NAMESPACE_STD
58+
59+
#endif // _LIBCPP___TYPE_TRAITS_IS_REPLACEABLE_H

libcxx/include/__utility/pair.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <__type_traits/is_implicitly_default_constructible.h>
3333
#include <__type_traits/is_nothrow_assignable.h>
3434
#include <__type_traits/is_nothrow_constructible.h>
35+
#include <__type_traits/is_replaceable.h>
3536
#include <__type_traits/is_same.h>
3637
#include <__type_traits/is_swappable.h>
3738
#include <__type_traits/is_trivially_relocatable.h>
@@ -75,6 +76,7 @@ struct _LIBCPP_TEMPLATE_VIS pair
7576
__conditional_t<__libcpp_is_trivially_relocatable<_T1>::value && __libcpp_is_trivially_relocatable<_T2>::value,
7677
pair,
7778
void>;
79+
using __replaceable = __conditional_t<__is_replaceable<_T1>::value && __is_replaceable<_T2>::value, pair, void>;
7880

7981
_LIBCPP_HIDE_FROM_ABI pair(pair const&) = default;
8082
_LIBCPP_HIDE_FROM_ABI pair(pair&&) = default;

libcxx/include/__vector/vector.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
#include <__type_traits/is_constructible.h>
5252
#include <__type_traits/is_nothrow_assignable.h>
5353
#include <__type_traits/is_nothrow_constructible.h>
54+
#include <__type_traits/is_replaceable.h>
5455
#include <__type_traits/is_same.h>
5556
#include <__type_traits/is_trivially_relocatable.h>
5657
#include <__type_traits/type_identity.h>
@@ -118,6 +119,10 @@ class _LIBCPP_TEMPLATE_VIS vector {
118119
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
119120
vector,
120121
void>;
122+
using __replaceable =
123+
__conditional_t<__is_replaceable<pointer>::value && __container_allocator_is_replaceable<__alloc_traits>::value,
124+
vector,
125+
void>;
121126

122127
static_assert(__check_valid_allocator<allocator_type>::value, "");
123128
static_assert(is_same<typename allocator_type::value_type, value_type>::value,

0 commit comments

Comments
 (0)