Skip to content

Commit 892ed1b

Browse files
committed
Fixes to the base facilities
1 parent b3b3d05 commit 892ed1b

File tree

4 files changed

+208
-24
lines changed

4 files changed

+208
-24
lines changed

libcxx/include/__memory/destroy.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
#include <__memory/addressof.h>
1414
#include <__memory/allocator_traits.h>
1515
#include <__memory/construct_at.h>
16-
#include <__memory/pointer_traits.h>
1716
#include <__utility/move.h>
1817

1918
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -48,7 +47,7 @@ template <class _Alloc, class _Iter, class _Sent>
4847
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void
4948
__allocator_destroy(_Alloc& __alloc, _Iter __first, _Sent __last) {
5049
for (; __first != __last; ++__first)
51-
allocator_traits<_Alloc>::destroy(__alloc, std::__to_address(__first));
50+
allocator_traits<_Alloc>::destroy(__alloc, std::addressof(*__first));
5251
}
5352

5453
template <class _ForwardIterator>

libcxx/include/__memory/relocate_at.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ _LIBCPP_HIDE_FROM_ABI _Tp* __libcpp_builtin_trivially_relocate_at(_Tp* __source,
4949
}
5050

5151
template <class _Tp>
52-
_LIBCPP_HIDE_FROM_ABI _Tp* __libcpp_builtin_trivially_relocate_at(_Tp* __first, Tp* __last, _Tp* __dest) _NOEXCEPT {
52+
_LIBCPP_HIDE_FROM_ABI _Tp* __libcpp_builtin_trivially_relocate_at(_Tp* __first, _Tp* __last, _Tp* __dest) _NOEXCEPT {
5353
static_assert(__libcpp_is_trivially_relocatable<_Tp>::value, "");
5454
// Casting to void* to suppress clang complaining that this is technically UB.
5555
__builtin_memmove(static_cast<void*>(__dest), __first, (__last - __first) * sizeof(_Tp));

libcxx/include/__memory/uninitialized_relocate.h

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111

1212
#include <__config>
1313
#include <__iterator/iterator_traits.h>
14+
#include <__memory/addressof.h>
1415
#include <__memory/allocator_traits.h>
16+
#include <__memory/destroy.h>
1517
#include <__memory/is_trivially_allocator_relocatable.h>
1618
#include <__memory/pointer_traits.h>
1719
#include <__memory/relocate_at.h>
@@ -30,8 +32,6 @@ _LIBCPP_PUSH_MACROS
3032

3133
_LIBCPP_BEGIN_NAMESPACE_STD
3234

33-
// TODO: We currently use std::__to_address but we don't guarantee contiguous iterators. How does that work?
34-
3535
// __uninitialized_relocate relocates the objects in [__first, __last) into __result.
3636
//
3737
// Relocation means that the objects in [__first, __last) are placed into __result as-if by move-construct and destroy,
@@ -49,7 +49,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
4949
// - __result contains the objects from [__first, __last)
5050
// - [__first, __last) doesn't contain any objects
5151
template <class _InputIter, class _NothrowForwardIter>
52-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _InputIter
52+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _NothrowForwardIter
5353
__uninitialized_relocate(_InputIter __first, _InputIter __last, _NothrowForwardIter __result) {
5454
if constexpr (__libcpp_is_contiguous_iterator<_InputIter>::value &&
5555
__libcpp_is_contiguous_iterator<_NothrowForwardIter>::value) {
@@ -64,10 +64,12 @@ __uninitialized_relocate(_InputIter __first, _InputIter __last, _NothrowForwardI
6464
if constexpr (__libcpp_is_contiguous_iterator<_InputIter>::value &&
6565
__libcpp_is_contiguous_iterator<_NothrowForwardIter>::value &&
6666
__libcpp_is_trivially_relocatable<_ValueType>::value) {
67-
// TODO: We might be able to memcpy if we don't overlap at all?
68-
std::__libcpp_builtin_trivially_relocate_at(
69-
std::__to_address(__first), std::__to_address(__last), std::__to_address(__result));
70-
return __result + (__last - __first);
67+
if (!__libcpp_is_constant_evaluated()) {
68+
// TODO: We might be able to memcpy if we don't overlap at all?
69+
std::__libcpp_builtin_trivially_relocate_at(
70+
std::__to_address(__first), std::__to_address(__last), std::__to_address(__result));
71+
return __result + (__last - __first);
72+
}
7173
}
7274

7375
// Otherwise, relocate elements one by one.
@@ -77,7 +79,7 @@ __uninitialized_relocate(_InputIter __first, _InputIter __last, _NothrowForwardI
7779
auto const __first_result = __result;
7880
try {
7981
while (__first != __last) {
80-
std::__relocate_at(std::__to_address(__first), std::__to_address(__result));
82+
std::__relocate_at(std::addressof(*__first), std::addressof(*__result));
8183
++__first;
8284
++__result;
8385
}
@@ -93,14 +95,13 @@ __uninitialized_relocate(_InputIter __first, _InputIter __last, _NothrowForwardI
9395
// the range [first, last) to another range ending at __result_last. The elements are relocated in reverse
9496
// order, but their relative order is preserved.
9597
template <class _BidirectionalIter, class _NothrowBidirectionalIter>
96-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _BidirectionalIter __uninitialized_relocate_backward(
98+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _NothrowBidirectionalIter __uninitialized_relocate_backward(
9799
_BidirectionalIter __first, _BidirectionalIter __last, _NothrowBidirectionalIter __result_last) {
98100
if constexpr (__libcpp_is_contiguous_iterator<_BidirectionalIter>::value &&
99101
__libcpp_is_contiguous_iterator<_NothrowBidirectionalIter>::value) {
100-
// TODO: Check for off-by-one here, we might want to check __result_last - 1
101102
_LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(
102103
!std::__is_pointer_in_range(
103-
std::__to_address(__first), std::__to_address(__last), std::__to_address(__result_last)),
104+
std::__to_address(__first), std::__to_address(__last), std::__to_address(__result_last) - 1),
104105
"uninitialized_relocate_backward requires the end of the result not to overlap with the input range");
105106
}
106107
using _ValueType = typename iterator_traits<_BidirectionalIter>::value_type;
@@ -110,11 +111,13 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _BidirectionalIter __uniniti
110111
if constexpr (__libcpp_is_contiguous_iterator<_BidirectionalIter>::value &&
111112
__libcpp_is_contiguous_iterator<_NothrowBidirectionalIter>::value &&
112113
__libcpp_is_trivially_relocatable<_ValueType>::value) {
113-
auto __result = __result_last - (__last - __first);
114-
// TODO: We might be able to memcpy if we don't overlap at all?
115-
std::__libcpp_builtin_trivially_relocate_at(
116-
std::__to_address(__first), std::__to_address(__last), std::__to_address(__result));
117-
return __result;
114+
if (!__libcpp_is_constant_evaluated()) {
115+
auto __result = __result_last - (__last - __first);
116+
// TODO: We might be able to memcpy if we don't overlap at all?
117+
std::__libcpp_builtin_trivially_relocate_at(
118+
std::__to_address(__first), std::__to_address(__last), std::__to_address(__result));
119+
return __result;
120+
}
118121
}
119122

120123
// Otherwise, relocate elements one by one, starting from the end.
@@ -126,7 +129,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _BidirectionalIter __uniniti
126129
while (__last != __first) {
127130
--__last;
128131
--__result;
129-
std::__relocate_at(std::__to_address(__last), std::__to_address(__result));
132+
std::__relocate_at(std::addressof(*__last), std::addressof(*__result));
130133
}
131134
} catch (...) {
132135
std::destroy(__first, __last);
@@ -156,7 +159,7 @@ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _NothrowForwardIter __uninit
156159
auto const __first_result = __result;
157160
try {
158161
while (__first != __last) {
159-
std::__allocator_relocate_at(__alloc, std::__to_address(__first), std::__to_address(__result));
162+
std::__allocator_relocate_at(__alloc, std::addressof(*__first), std::addressof(*__result));
160163
++__first;
161164
++__result;
162165
}
@@ -176,10 +179,9 @@ __uninitialized_allocator_relocate_backward(
176179
_Alloc& __alloc, _BidirectionalIter __first, _BidirectionalIter __last, _NothrowBidirectionalIter __result_last) {
177180
if constexpr (__libcpp_is_contiguous_iterator<_BidirectionalIter>::value &&
178181
__libcpp_is_contiguous_iterator<_NothrowBidirectionalIter>::value) {
179-
// TODO: Off by one?
180182
_LIBCPP_ASSERT_NON_OVERLAPPING_RANGES(
181183
!std::__is_pointer_in_range(
182-
std::__to_address(__first), std::__to_address(__last), std::__to_address(__result_last)),
184+
std::__to_address(__first), std::__to_address(__last), std::__to_address(__result_last) - 1),
183185
"uninitialized_allocator_relocate_backward requires the end of the result not to overlap with the input range");
184186
}
185187

@@ -194,7 +196,7 @@ __uninitialized_allocator_relocate_backward(
194196
while (__last != __first) {
195197
--__last;
196198
--__result;
197-
std::__allocator_relocate_at(__alloc, std::__to_address(__last), std::__to_address(__result));
199+
std::__allocator_relocate_at(__alloc, std::addressof(*__last), std::addressof(*__result));
198200
}
199201
} catch (...) {
200202
std::__allocator_destroy(__alloc, __first, __last);
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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+
// Test the std::__uninitialized_allocator_relocate internal algorithm.
10+
11+
#include <__memory/uninitialized_relocate.h>
12+
#include <cassert>
13+
#include <cstddef>
14+
#include <memory>
15+
16+
#include "test_macros.h"
17+
#include "test_iterators.h"
18+
#include "min_allocator.h"
19+
20+
template <class Allocator>
21+
struct Fixture {
22+
using Traits = std::allocator_traits<Allocator>;
23+
using ValueType = typename Traits::value_type;
24+
using Pointer = typename Traits::pointer;
25+
26+
constexpr Fixture(std::size_t n) : size(n) {
27+
source = allocator.allocate(n);
28+
for (std::size_t i = 0; i != n; ++i) {
29+
Traits::construct(allocator, std::to_address(source + i), ValueType(i));
30+
}
31+
32+
dest = allocator.allocate(n);
33+
}
34+
35+
constexpr void relocated(std::size_t n) { relocated_ = n; }
36+
37+
constexpr ~Fixture() {
38+
for (std::size_t i = 0; i != relocated_; ++i) {
39+
Traits::destroy(allocator, std::to_address(dest + i));
40+
}
41+
allocator.deallocate(dest, size);
42+
43+
for (std::size_t i = relocated_; i != size; ++i) {
44+
Traits::destroy(allocator, std::to_address(source + i));
45+
}
46+
allocator.deallocate(source, size);
47+
}
48+
49+
Allocator allocator;
50+
std::size_t size;
51+
std::size_t relocated_ = 0;
52+
Pointer source;
53+
Pointer dest;
54+
};
55+
56+
template <class Alloc, class Iterator, class OutputIterator>
57+
constexpr void test() {
58+
using T = std::allocator_traits<Alloc>::value_type;
59+
using Pointer = std::allocator_traits<Alloc>::pointer;
60+
61+
// Relocate an empty range
62+
{
63+
Fixture<Alloc> t(10);
64+
65+
OutputIterator res = std::__uninitialized_allocator_relocate(
66+
t.allocator,
67+
Iterator(std::to_address(t.source)),
68+
Iterator(std::to_address(t.source)),
69+
OutputIterator(std::to_address(t.dest)));
70+
assert(res == OutputIterator(std::to_address(t.dest)));
71+
t.relocated(0);
72+
73+
for (int i = 0; i != 10; ++i) {
74+
assert(t.source[i] == T(i));
75+
}
76+
}
77+
78+
// Range of size 1
79+
{
80+
Fixture<Alloc> t(10);
81+
82+
OutputIterator res = std::__uninitialized_allocator_relocate(
83+
t.allocator,
84+
Iterator(std::to_address(t.source)),
85+
Iterator(std::to_address(t.source + 1)),
86+
OutputIterator(std::to_address(t.dest)));
87+
assert(res == OutputIterator(std::to_address(t.dest + 1)));
88+
t.relocated(1);
89+
90+
assert(t.dest[0] == T(0));
91+
assert(t.source[1] == T(1));
92+
assert(t.source[2] == T(2));
93+
// ...
94+
}
95+
96+
// Range of normal size
97+
{
98+
Fixture<Alloc> t(10);
99+
100+
OutputIterator res = std::__uninitialized_allocator_relocate(
101+
t.allocator,
102+
Iterator(std::to_address(t.source)),
103+
Iterator(std::to_address(t.source + 10)),
104+
OutputIterator(std::to_address(t.dest)));
105+
assert(res == OutputIterator(std::to_address(t.dest + 10)));
106+
t.relocated(10);
107+
108+
for (int i = 0; i != 10; ++i) {
109+
assert(t.dest[i] == T(i));
110+
}
111+
}
112+
113+
// Relocate with some overlap between the input and the output range
114+
{
115+
Alloc allocator;
116+
Pointer buff = allocator.allocate(10);
117+
for (std::size_t i = 5; i != 10; ++i) {
118+
std::allocator_traits<Alloc>::construct(allocator, std::to_address(buff + i), T(i));
119+
}
120+
121+
OutputIterator res = std::__uninitialized_allocator_relocate(
122+
allocator,
123+
Iterator(std::to_address(buff + 5)),
124+
Iterator(std::to_address(buff + 10)),
125+
OutputIterator(std::to_address(buff)));
126+
assert(res == OutputIterator(std::to_address(buff + 5)));
127+
128+
for (int i = 0; i != 5; ++i) {
129+
assert(buff[i] == T(i + 5));
130+
std::allocator_traits<Alloc>::destroy(allocator, std::to_address(buff + i));
131+
}
132+
allocator.deallocate(buff, 10);
133+
}
134+
135+
// TODO: Add exception test
136+
}
137+
138+
struct NotTriviallyRelocatable {
139+
constexpr explicit NotTriviallyRelocatable(int i) : value_(i) {}
140+
constexpr NotTriviallyRelocatable(NotTriviallyRelocatable&& other) : value_(other.value_) { other.value_ = -1; }
141+
constexpr friend bool operator==(NotTriviallyRelocatable const& a, NotTriviallyRelocatable const& b) {
142+
return a.value_ == b.value_;
143+
}
144+
145+
int value_;
146+
};
147+
148+
template <class T>
149+
struct ConsructAllocator : std::allocator<T> {
150+
template < class... Args>
151+
constexpr void construct(T* loc, Args&&... args) {
152+
std::construct_at(loc, std::forward<Args>(args)...);
153+
}
154+
};
155+
156+
template <class T>
157+
struct DestroyAllocator : std::allocator<T> {
158+
constexpr void destroy(T* loc) { std::destroy_at(loc); }
159+
};
160+
161+
constexpr bool tests() {
162+
test<std::allocator<int>, cpp17_input_iterator<int*>, forward_iterator<int*>>();
163+
test<std::allocator<int>, contiguous_iterator<int*>, contiguous_iterator<int*>>();
164+
test<min_allocator<int>, cpp17_input_iterator<int*>, forward_iterator<int*>>();
165+
test<std::allocator<NotTriviallyRelocatable>,
166+
cpp17_input_iterator<NotTriviallyRelocatable*>,
167+
forward_iterator<NotTriviallyRelocatable*>>();
168+
test<ConsructAllocator<NotTriviallyRelocatable>,
169+
cpp17_input_iterator<NotTriviallyRelocatable*>,
170+
forward_iterator<NotTriviallyRelocatable*>>();
171+
test<DestroyAllocator<NotTriviallyRelocatable>,
172+
cpp17_input_iterator<NotTriviallyRelocatable*>,
173+
forward_iterator<NotTriviallyRelocatable*>>();
174+
return true;
175+
}
176+
177+
int main(int, char**) {
178+
tests();
179+
#if TEST_STD_VER >= 20
180+
static_assert(tests(), "");
181+
#endif
182+
return 0;
183+
}

0 commit comments

Comments
 (0)