Skip to content

Commit eb12d9b

Browse files
authored
[libc++] Remove the allocator<const T> extension (#96319)
This effort has quite a history: - This was first attempted in 2022 via bed3240, which broke std::shared_ptr<T const> and caused the change to be reverted in 9138666. - We then re-attempted landing the change in 276ca87 after fixing std::shared_ptr, but reports were made that this broke code en masse within Google. This led to the patch being reverted again in a54d028 with the goal to land this again with a migration path for vendors. This patch re-lands the removal while providing a migration path for vendors by providing the `_LIBCPP_ENABLE_REMOVED_ALLOCATOR_CONST` macro. This macro will be honored for the LLVM 19 release and will be removed after that, at which point allocator<const T> will be removed unconditionally. Fixes #73665
1 parent 54b61ad commit eb12d9b

19 files changed

+42
-118
lines changed

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,15 @@ Deprecations and Removals
121121
- The ``_LIBCPP_DISABLE_ADDITIONAL_DIAGNOSTICS`` macro has been removed and is not honored anymore. Additional
122122
warnings provided by libc++ as a matter of QoI will now be provided unconditionally.
123123

124+
- libc++ no longer supports ``std::allocator<const T>`` and containers of ``const``-qualified element type, such
125+
as ``std::vector<const T>`` and ``std::list<const T>``. This used to be supported as an undocumented extension.
126+
If you were using ``std::vector<const T>``, replace it with ``std::vector<T>`` instead. The
127+
``_LIBCPP_ENABLE_REMOVED_ALLOCATOR_CONST`` macro can be defined to temporarily re-enable this extension as
128+
folks transition their code. This macro will be honored for one released and ignored starting in LLVM 20.
129+
To assist with the clean-up process, consider running your code through Clang Tidy, with
130+
`std-allocator-const <https://clang.llvm.org/extra/clang-tidy/checks/portability/std-allocator-const.html>`
131+
enabled.
132+
124133

125134
Upcoming Deprecations and Removals
126135
----------------------------------

libcxx/include/__memory/allocator.h

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <__memory/addressof.h>
1515
#include <__memory/allocate_at_least.h>
1616
#include <__memory/allocator_traits.h>
17+
#include <__type_traits/is_const.h>
1718
#include <__type_traits/is_constant_evaluated.h>
1819
#include <__type_traits/is_same.h>
1920
#include <__type_traits/is_void.h>
@@ -36,8 +37,6 @@ class allocator;
3637
// Specializing allocator<void> is deprecated, but not using it.
3738
template <>
3839
class _LIBCPP_TEMPLATE_VIS allocator<void> {
39-
# if _LIBCPP_STD_VER <= 17
40-
4140
public:
4241
_LIBCPP_DEPRECATED_IN_CXX17 typedef void* pointer;
4342
_LIBCPP_DEPRECATED_IN_CXX17 typedef const void* const_pointer;
@@ -47,13 +46,12 @@ class _LIBCPP_TEMPLATE_VIS allocator<void> {
4746
struct _LIBCPP_DEPRECATED_IN_CXX17 rebind {
4847
typedef allocator<_Up> other;
4948
};
50-
# endif
5149
};
5250

51+
// TODO(LLVM 20): Remove the escape hatch
52+
# ifdef _LIBCPP_ENABLE_REMOVED_ALLOCATOR_CONST
5353
template <>
5454
class _LIBCPP_TEMPLATE_VIS allocator<const void> {
55-
# if _LIBCPP_STD_VER <= 17
56-
5755
public:
5856
_LIBCPP_DEPRECATED_IN_CXX17 typedef const void* pointer;
5957
_LIBCPP_DEPRECATED_IN_CXX17 typedef const void* const_pointer;
@@ -63,9 +61,9 @@ class _LIBCPP_TEMPLATE_VIS allocator<const void> {
6361
struct _LIBCPP_DEPRECATED_IN_CXX17 rebind {
6462
typedef allocator<_Up> other;
6563
};
66-
# endif
6764
};
68-
#endif
65+
# endif // _LIBCPP_ENABLE_REMOVED_ALLOCATOR_CONST
66+
#endif // _LIBCPP_STD_VER <= 17
6967

7068
// This class provides a non-trivial default constructor to the class that derives from it
7169
// if the condition is satisfied.
@@ -94,6 +92,7 @@ struct __non_trivial_if<true, _Unique> {
9492

9593
template <class _Tp>
9694
class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::value, allocator<_Tp> > {
95+
static_assert(!is_const<_Tp>::value, "std::allocator does not support const types");
9796
static_assert(!is_volatile<_Tp>::value, "std::allocator does not support volatile types");
9897

9998
public:
@@ -170,6 +169,8 @@ class _LIBCPP_TEMPLATE_VIS allocator : private __non_trivial_if<!is_void<_Tp>::v
170169
#endif
171170
};
172171

172+
// TODO(LLVM 20): Remove the escape hatch
173+
#ifdef _LIBCPP_ENABLE_REMOVED_ALLOCATOR_CONST
173174
template <class _Tp>
174175
class _LIBCPP_TEMPLATE_VIS allocator<const _Tp>
175176
: private __non_trivial_if<!is_void<_Tp>::value, allocator<const _Tp> > {
@@ -180,9 +181,9 @@ class _LIBCPP_TEMPLATE_VIS allocator<const _Tp>
180181
typedef ptrdiff_t difference_type;
181182
typedef const _Tp value_type;
182183
typedef true_type propagate_on_container_move_assignment;
183-
#if _LIBCPP_STD_VER <= 23 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_ALLOCATOR_MEMBERS)
184+
# if _LIBCPP_STD_VER <= 23 || defined(_LIBCPP_ENABLE_CXX26_REMOVED_ALLOCATOR_MEMBERS)
184185
_LIBCPP_DEPRECATED_IN_CXX23 typedef true_type is_always_equal;
185-
#endif
186+
# endif
186187

187188
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 allocator() _NOEXCEPT = default;
188189

@@ -199,11 +200,11 @@ class _LIBCPP_TEMPLATE_VIS allocator<const _Tp>
199200
}
200201
}
201202

202-
#if _LIBCPP_STD_VER >= 23
203+
# if _LIBCPP_STD_VER >= 23
203204
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr allocation_result<const _Tp*> allocate_at_least(size_t __n) {
204205
return {allocate(__n), __n};
205206
}
206-
#endif
207+
# endif
207208

208209
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void deallocate(const _Tp* __p, size_t __n) {
209210
if (__libcpp_is_constant_evaluated()) {
@@ -214,7 +215,7 @@ class _LIBCPP_TEMPLATE_VIS allocator<const _Tp>
214215
}
215216

216217
// C++20 Removed members
217-
#if _LIBCPP_STD_VER <= 17
218+
# if _LIBCPP_STD_VER <= 17
218219
_LIBCPP_DEPRECATED_IN_CXX17 typedef const _Tp* pointer;
219220
_LIBCPP_DEPRECATED_IN_CXX17 typedef const _Tp* const_pointer;
220221
_LIBCPP_DEPRECATED_IN_CXX17 typedef const _Tp& reference;
@@ -243,8 +244,9 @@ class _LIBCPP_TEMPLATE_VIS allocator<const _Tp>
243244
}
244245

245246
_LIBCPP_DEPRECATED_IN_CXX17 _LIBCPP_HIDE_FROM_ABI void destroy(pointer __p) { __p->~_Tp(); }
246-
#endif
247+
# endif
247248
};
249+
#endif // _LIBCPP_ENABLE_REMOVED_ALLOCATOR_CONST
248250

249251
template <class _Tp, class _Up>
250252
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool

libcxx/include/__memory/shared_ptr.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ struct __shared_ptr_emplace : __shared_weak_count {
259259
class _Allocator = _Alloc,
260260
__enable_if_t<!is_same<typename _Allocator::value_type, __for_overwrite_tag>::value, int> = 0>
261261
_LIBCPP_HIDE_FROM_ABI explicit __shared_ptr_emplace(_Alloc __a, _Args&&... __args) : __storage_(std::move(__a)) {
262-
using _TpAlloc = typename __allocator_traits_rebind<_Alloc, _Tp>::type;
262+
using _TpAlloc = typename __allocator_traits_rebind<_Alloc, __remove_cv_t<_Tp> >::type;
263263
_TpAlloc __tmp(*__get_alloc());
264264
allocator_traits<_TpAlloc>::construct(__tmp, __get_elem(), std::forward<_Args>(__args)...);
265265
}
@@ -278,7 +278,7 @@ struct __shared_ptr_emplace : __shared_weak_count {
278278
template <class _Allocator = _Alloc,
279279
__enable_if_t<!is_same<typename _Allocator::value_type, __for_overwrite_tag>::value, int> = 0>
280280
_LIBCPP_HIDE_FROM_ABI void __on_zero_shared_impl() _NOEXCEPT {
281-
using _TpAlloc = typename __allocator_traits_rebind<_Allocator, _Tp>::type;
281+
using _TpAlloc = typename __allocator_traits_rebind<_Allocator, __remove_cv_t<_Tp> >::type;
282282
_TpAlloc __tmp(*__get_alloc());
283283
allocator_traits<_TpAlloc>::destroy(__tmp, __get_elem());
284284
}
@@ -598,8 +598,8 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr {
598598
#if _LIBCPP_STD_VER <= 14 || defined(_LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR)
599599
template <class _Yp, __enable_if_t<is_convertible<_Yp*, element_type*>::value, int> = 0>
600600
_LIBCPP_HIDE_FROM_ABI shared_ptr(auto_ptr<_Yp>&& __r) : __ptr_(__r.get()) {
601-
typedef __shared_ptr_pointer<_Yp*, default_delete<_Yp>, allocator<_Yp> > _CntrlBlk;
602-
__cntrl_ = new _CntrlBlk(__r.get(), default_delete<_Yp>(), allocator<_Yp>());
601+
typedef __shared_ptr_pointer<_Yp*, default_delete<_Yp>, allocator<__remove_cv_t<_Yp> > > _CntrlBlk;
602+
__cntrl_ = new _CntrlBlk(__r.get(), default_delete<_Yp>(), allocator<__remove_cv_t<_Yp> >());
603603
__enable_weak_this(__r.get(), __r.get());
604604
__r.release();
605605
}
@@ -776,7 +776,7 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS shared_ptr {
776776
private:
777777
template <class _Yp, bool = is_function<_Yp>::value>
778778
struct __shared_ptr_default_allocator {
779-
typedef allocator<_Yp> type;
779+
typedef allocator<__remove_cv_t<_Yp> > type;
780780
};
781781

782782
template <class _Yp>
@@ -834,7 +834,7 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared(const _Alloc& __a, _Args&&
834834

835835
template <class _Tp, class... _Args, __enable_if_t<!is_array<_Tp>::value, int> = 0>
836836
_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared(_Args&&... __args) {
837-
return std::allocate_shared<_Tp>(allocator<_Tp>(), std::forward<_Args>(__args)...);
837+
return std::allocate_shared<_Tp>(allocator<__remove_cv_t<_Tp> >(), std::forward<_Args>(__args)...);
838838
}
839839

840840
#if _LIBCPP_STD_VER >= 20
@@ -848,7 +848,7 @@ _LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> allocate_shared_for_overwrite(const _Alloc
848848

849849
template <class _Tp, __enable_if_t<!is_array<_Tp>::value, int> = 0>
850850
_LIBCPP_HIDE_FROM_ABI shared_ptr<_Tp> make_shared_for_overwrite() {
851-
return std::allocate_shared_for_overwrite<_Tp>(allocator<_Tp>());
851+
return std::allocate_shared_for_overwrite<_Tp>(allocator<__remove_cv_t<_Tp>>());
852852
}
853853

854854
#endif // _LIBCPP_STD_VER >= 20

libcxx/test/libcxx/containers/sequences/vector/const_T.compile.pass.cpp

Lines changed: 0 additions & 18 deletions
This file was deleted.

libcxx/test/libcxx/containers/sequences/vector/const_value_type.pass.cpp

Lines changed: 0 additions & 25 deletions
This file was deleted.

libcxx/test/libcxx/diagnostics/memory.nodiscard.verify.cpp

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,3 @@ void test_allocator() {
3939
allocator.allocate_at_least(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
4040
#endif
4141
}
42-
43-
void test_const_allocator() {
44-
std::allocator<const int> allocator;
45-
allocator.allocate(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
46-
47-
#if TEST_STD_VER <= 17
48-
allocator.allocate(1, nullptr); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
49-
#endif
50-
#if TEST_STD_VER >= 23
51-
allocator.allocate_at_least(1); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
52-
#endif
53-
}

libcxx/test/libcxx/memory/allocator_void.trivial.compile.pass.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,10 @@
1717
#include <type_traits>
1818

1919
typedef std::allocator<void> A1;
20-
typedef std::allocator<void const> A2;
21-
struct A3 : std::allocator<void> { };
22-
struct A4 : std::allocator<void const> { };
20+
struct A2 : std::allocator<void> { };
2321

2422
static_assert(std::is_trivially_default_constructible<A1>::value, "");
2523
static_assert(std::is_trivial<A1>::value, "");
2624

2725
static_assert(std::is_trivially_default_constructible<A2>::value, "");
2826
static_assert(std::is_trivial<A2>::value, "");
29-
30-
static_assert(std::is_trivially_default_constructible<A3>::value, "");
31-
static_assert(std::is_trivial<A3>::value, "");
32-
33-
static_assert(std::is_trivially_default_constructible<A4>::value, "");
34-
static_assert(std::is_trivial<A4>::value, "");

libcxx/test/libcxx/memory/allocator_volatile.verify.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
// http://wg21.link/LWG2447 gives implementors freedom to reject volatile types in `std::allocator`.
9+
// http://wg21.link/LWG2447 gives implementors freedom to reject const or volatile types in `std::allocator`.
1010

1111
#include <memory>
1212

13-
std::allocator<volatile int> A1; // expected-error@*:* {{std::allocator does not support volatile types}}
14-
std::allocator<const volatile int> A2; // expected-error@*:* {{std::allocator does not support volatile types}}
13+
std::allocator<const int> A1; // expected-error@*:* {{std::allocator does not support const types}}
14+
std::allocator<volatile int> A2; // expected-error@*:* {{std::allocator does not support volatile types}}

libcxx/test/std/concepts/concepts.lang/concept.default.init/default_initializable.compile.pass.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -205,9 +205,6 @@ void test()
205205
test_not_const<std::array< volatile int, 1>>();
206206
test_false <std::array<const volatile int, 1>>();
207207
test_true <std::deque< int>>();
208-
#ifdef _LIBCPP_VERSION
209-
test_true <std::deque<const int>>();
210-
#endif // _LIBCPP_VERSION
211208
test_true <std::forward_list<int>>();
212209
test_true <std::list<int>>();
213210
test_true <std::vector<int>>();
@@ -226,9 +223,6 @@ void test()
226223

227224
// Container adaptors
228225
test_true <std::stack< int>>();
229-
#ifdef _LIBCPP_VERSION
230-
test_true <std::stack<const int>>();
231-
#endif // _LIBCPP_VERSION
232226
test_true <std::queue<int>>();
233227
test_true <std::priority_queue<int>>();
234228

libcxx/test/std/utilities/memory/default.allocator/allocator.ctor.pass.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ TEST_CONSTEXPR_CXX20 bool test() {
3737

3838
int main(int, char**) {
3939
test<char>();
40-
test<char const>();
40+
test<int>();
4141
test<void>();
4242

4343
#if TEST_STD_VER > 17
4444
static_assert(test<char>());
45-
static_assert(test<char const>());
45+
static_assert(test<int>());
4646
static_assert(test<void>());
4747
#endif
4848
return 0;

libcxx/test/std/utilities/memory/default.allocator/allocator.dtor.pass.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,9 @@ int main(int, char**)
2626
{
2727
test<int>();
2828
test<void>();
29-
#ifdef _LIBCPP_VERSION // extension
30-
test<int const>();
31-
#endif // _LIBCPP_VERSION
3229

3330
static_assert(test<int>());
3431
static_assert(test<void>());
35-
#ifdef _LIBCPP_VERSION // extension
36-
static_assert(test<int const>());
37-
#endif // _LIBCPP_VERSION
3832

3933
return 0;
4034
}

libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.constexpr.size.verify.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,4 @@ constexpr bool test()
3434

3535
void f() {
3636
static_assert(test<double>()); // expected-error {{static assertion expression is not an integral constant expression}}
37-
LIBCPP_STATIC_ASSERT(test<const double>()); // expected-error {{static assertion expression is not an integral constant expression}}
3837
}

libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.size.pass.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ void test()
4545
int main(int, char**)
4646
{
4747
test<double>();
48-
LIBCPP_ONLY(test<const double>());
4948

5049
return 0;
5150
}

libcxx/test/std/utilities/memory/default.allocator/allocator_types.deprecated_in_cxx17.verify.cpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,6 @@ void f() {
3535
typedef std::allocator<char>::const_reference ConstReference; // expected-warning {{'const_reference' is deprecated}}
3636
typedef std::allocator<char>::rebind<int>::other Rebind; // expected-warning {{'rebind<int>' is deprecated}}
3737
}
38-
{
39-
typedef std::allocator<char const>::pointer Pointer; // expected-warning {{'pointer' is deprecated}}
40-
typedef std::allocator<char const>::const_pointer ConstPointer; // expected-warning {{'const_pointer' is deprecated}}
41-
typedef std::allocator<char const>::reference Reference; // expected-warning {{'reference' is deprecated}}
42-
typedef std::allocator<char const>::const_reference ConstReference; // expected-warning {{'const_reference' is deprecated}}
43-
typedef std::allocator<char const>::rebind<int>::other Rebind; // expected-warning {{'rebind<int>' is deprecated}}
44-
}
4538
{
4639
typedef std::allocator<void>::pointer Pointer; // expected-warning {{'pointer' is deprecated}}
4740
typedef std::allocator<void>::const_pointer ConstPointer; // expected-warning {{'const_pointer' is deprecated}}

libcxx/test/std/utilities/memory/default.allocator/allocator_types.deprecated_in_cxx23.verify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ void test() {
2525
typedef std::allocator<char>::is_always_equal IAE; // expected-warning {{'is_always_equal' is deprecated}}
2626
}
2727
{
28-
typedef std::allocator<const char>::is_always_equal IAE; // expected-warning {{'is_always_equal' is deprecated}}
28+
typedef std::allocator<int>::is_always_equal IAE; // expected-warning {{'is_always_equal' is deprecated}}
2929
}
3030
{
3131
typedef std::allocator<void>::is_always_equal IAE; // expected-warning {{'is_always_equal' is deprecated}}

libcxx/test/std/utilities/memory/default.allocator/allocator_types.pass.cpp

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,5 @@ void test() {
6161

6262
int main(int, char**) {
6363
test<char>();
64-
#ifdef _LIBCPP_VERSION
65-
test<char const>(); // extension
66-
#endif
6764
return 0;
6865
}

libcxx/test/std/utilities/memory/default.allocator/allocator_types.removed_in_cxx20.verify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,6 @@ void check()
3939

4040
void f() {
4141
check<char>();
42-
check<char const>();
42+
check<int>();
4343
check<void>();
4444
}

libcxx/test/std/utilities/memory/default.allocator/allocator_types.removed_in_cxx26.verify.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,6 @@ void check() {
2929

3030
void test() {
3131
check<char>();
32-
check<char const>();
32+
check<int>();
3333
check<void>();
3434
}

0 commit comments

Comments
 (0)