Skip to content

Commit 79a8d46

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 when implementing some container operations in terms of relocation. Eventually, the library "emulation" added by this patch can be replaced once the compiler implements the trivial relocation proposals.
1 parent c4eec9e commit 79a8d46

File tree

18 files changed

+391
-2
lines changed

18 files changed

+391
-2
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ set(files
838838
__type_traits/is_reference.h
839839
__type_traits/is_reference_wrapper.h
840840
__type_traits/is_referenceable.h
841+
__type_traits/is_replaceable.h
841842
__type_traits/is_same.h
842843
__type_traits/is_scalar.h
843844
__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
@@ -65,8 +65,11 @@ class _LIBCPP_EXPORTED_FROM_ABI exception_ptr {
6565
friend _LIBCPP_HIDE_FROM_ABI exception_ptr make_exception_ptr(_Ep) _NOEXCEPT;
6666

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

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

libcxx/include/__expected/expected.h

Lines changed: 3 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,8 @@ 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 _LIBCPP_NODEBUG =
475+
__conditional_t<__is_replaceable<_Tp>::value && __is_replaceable<_Err>::value, expected, void>;
473476

474477
template <class _Up>
475478
using rebind = expected<_Up, error_type>;

libcxx/include/__locale

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

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

5659
// types:
5760
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
@@ -317,7 +317,11 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr {
317317

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

322326
private:
323327
element_type* __ptr_;
@@ -1212,7 +1216,10 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS weak_ptr {
12121216

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

12171224
private:
12181225
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>
@@ -144,6 +145,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr {
144145
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<deleter_type>::value,
145146
unique_ptr,
146147
void>;
148+
using __replaceable _LIBCPP_NODEBUG =
149+
__conditional_t<__is_replaceable<pointer>::value && __is_replaceable<deleter_type>::value, unique_ptr, void>;
147150

148151
private:
149152
_LIBCPP_COMPRESSED_PAIR(pointer, __ptr_, deleter_type, __deleter_);
@@ -410,6 +413,8 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp>
410413
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<deleter_type>::value,
411414
unique_ptr,
412415
void>;
416+
using __replaceable _LIBCPP_NODEBUG =
417+
__conditional_t<__is_replaceable<pointer>::value && __is_replaceable<deleter_type>::value, unique_ptr, void>;
413418

414419
private:
415420
template <class _Up, class _OtherDeleter>

libcxx/include/__split_buffer

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <__type_traits/integral_constant.h>
2929
#include <__type_traits/is_nothrow_assignable.h>
3030
#include <__type_traits/is_nothrow_constructible.h>
31+
#include <__type_traits/is_replaceable.h>
3132
#include <__type_traits/is_swappable.h>
3233
#include <__type_traits/is_trivially_destructible.h>
3334
#include <__type_traits/is_trivially_relocatable.h>
@@ -72,6 +73,10 @@ public:
7273
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
7374
__split_buffer,
7475
void>;
76+
using __replaceable _LIBCPP_NODEBUG =
77+
__conditional_t<__is_replaceable<pointer>::value && __container_allocator_is_replaceable<__alloc_traits>::value,
78+
__split_buffer,
79+
void>;
7580

7681
pointer __first_;
7782
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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include <__type_traits/is_implicitly_default_constructible.h>
3232
#include <__type_traits/is_nothrow_assignable.h>
3333
#include <__type_traits/is_nothrow_constructible.h>
34+
#include <__type_traits/is_replaceable.h>
35+
#include <__type_traits/is_same.h>
3436
#include <__type_traits/is_swappable.h>
3537
#include <__type_traits/is_trivially_relocatable.h>
3638
#include <__type_traits/nat.h>
@@ -72,6 +74,8 @@ struct _LIBCPP_TEMPLATE_VIS pair
7274
__conditional_t<__libcpp_is_trivially_relocatable<_T1>::value && __libcpp_is_trivially_relocatable<_T2>::value,
7375
pair,
7476
void>;
77+
using __replaceable _LIBCPP_NODEBUG =
78+
__conditional_t<__is_replaceable<_T1>::value && __is_replaceable<_T2>::value, pair, void>;
7579

7680
_LIBCPP_HIDE_FROM_ABI pair(pair const&) = default;
7781
_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
@@ -55,6 +55,7 @@
5555
#include <__type_traits/is_nothrow_assignable.h>
5656
#include <__type_traits/is_nothrow_constructible.h>
5757
#include <__type_traits/is_pointer.h>
58+
#include <__type_traits/is_replaceable.h>
5859
#include <__type_traits/is_same.h>
5960
#include <__type_traits/is_trivially_relocatable.h>
6061
#include <__type_traits/type_identity.h>
@@ -120,6 +121,10 @@ class _LIBCPP_TEMPLATE_VIS vector {
120121
__libcpp_is_trivially_relocatable<pointer>::value && __libcpp_is_trivially_relocatable<allocator_type>::value,
121122
vector,
122123
void>;
124+
using __replaceable _LIBCPP_NODEBUG =
125+
__conditional_t<__is_replaceable<pointer>::value && __container_allocator_is_replaceable<__alloc_traits>::value,
126+
vector,
127+
void>;
123128

124129
static_assert(__check_valid_allocator<allocator_type>::value, "");
125130
static_assert(is_same<typename allocator_type::value_type, value_type>::value,

0 commit comments

Comments
 (0)