[libcxx-commits] [libcxx] [libcxx][string] Allow fancy pointers in basic_string (PR #199264)
via libcxx-commits
libcxx-commits at lists.llvm.org
Fri May 22 22:58:35 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-libcxx
Author: Nikita Belenkiy (kitsnet)
<details>
<summary>Changes</summary>
The rationale for this change is to allow allocation of strings in interprocess shared memory
using fancy pointers like boost::interprocess::offset_ptr.
Currently, basic_string does not support custom allocators which use fancy pointers
that are not trivially constructible and destructible as the pointer type. This happens
because in its internal representation, fancy pointers are stored in a union, which
deletes the default constructor of such a union.
In order to allow fancy pointers, this PR does:
* create an alternative version of the underlying union for fancy pointers, with
explicitly defined special member functions. The original version (compile-time selected)
is kept to avoid performance regression with non-fancy pointers;
* add explicit __construct_at() to start lifetime of the fancy pointer in constant expressions;
* simplifies the logic of self-move-assignment handling;
* adds tests usmng fanci_polinter_allocator in libcxx/test/libcxx/strings/
Added tests in libcxx/test/std/strings/ also exist and pass, but, being much more numerous,
will be provided in a separate PR to simplify review of this PR.
Also, the PR code initially contained fixes for deficiencies in short string ASAN annotations,
but as short string ASAN annotations are currently disabled and are going to be removed or
reworked, I have removed these fixes to avoid potential merge conflicts.
Fixes: https://github.com/llvm/llvm-project/issues/20882
---
Patch is 50.64 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/199264.diff
27 Files Affected:
- (modified) libcxx/include/string (+93-29)
- (modified) libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp (+15)
- (modified) libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp (+35)
- (modified) libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.access/assert.cindex.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.access/assert.front.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.access/assert.index.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.cons/copy_shrunk_long.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.add.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.decrement.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.dereference.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.increment.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.iterators/assert.iterator.index.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.compare.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.iterators/debug.iterator.subtract.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.erase_iter.null.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.modifiers/assert.pop_back.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.erase.iter_iter.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_char.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_iter_iter.pass.cpp (+2)
- (modified) libcxx/test/libcxx/strings/basic.string/string.modifiers/debug.insert.iter_size_char.pass.cpp (+2)
- (modified) libcxx/test/std/strings/basic.string/string.cons/move_noexcept.pass.cpp (-4)
- (modified) libcxx/test/support/module.modulemap (+4-1)
- (modified) libcxx/test/support/test_allocator.h (+399-12)
``````````diff
diff --git a/libcxx/include/string b/libcxx/include/string
index 2455938a92d9c..8a0792a5b8028 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -637,8 +637,10 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
# include <__type_traits/is_nothrow_constructible.h>
# include <__type_traits/is_same.h>
# include <__type_traits/is_standard_layout.h>
+# include <__type_traits/is_trivially_assignable.h>
# include <__type_traits/is_trivially_constructible.h>
# include <__type_traits/is_trivially_copyable.h>
+# include <__type_traits/is_trivially_destructible.h>
# include <__type_traits/is_trivially_relocatable.h>
# include <__type_traits/remove_cv.h>
# include <__type_traits/remove_cvref.h>
@@ -746,7 +748,7 @@ public:
using const_pointer = typename __alloc_traits::const_pointer;
// A basic_string contains the following members which may be trivially relocatable:
- // - pointer: is currently assumed to be trivially relocatable, but is still checked in case that changes
+ // - pointer: may or may not be trivially relocatable, so it's checked
// - size_type: is always trivially relocatable, since it has to be an integral type
// - value_type: is always trivially relocatable, since it has to be trivial
// - unsigned char: is a fundamental type, so it's trivially relocatable
@@ -875,16 +877,95 @@ private:
static_assert(sizeof(__short) == (sizeof(value_type) * (__min_cap + 1)), "__short has an unexpected size.");
- union __rep {
+ template <typename __long_t, typename __t = void>
+ union __rep_t {
__short __s;
- __long __l;
+ __long_t __l;
- __rep() = default;
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep(__short __r) : __s(__r) {}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep(__long __r) : __l(__r) {}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep(__uninitialized_tag) {}
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __begin_long_lifetime() {}
+
+ __rep_t() = default;
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__short __r) : __s(__r) {}
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__long __r) : __l(__r) {}
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__uninitialized_tag) {}
+ };
+
+ template <typename __long_t>
+ union __rep_t<__long_t,
+ typename std::enable_if<!is_trivially_constructible<__long_t>::value ||
+ !is_trivially_copy_assignable<__long_t>::value ||
+ !is_trivially_destructible<__long_t>::value >::type> {
+ __short __s;
+ __long_t __l;
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 bool __is_long() const _NOEXCEPT {
+ if (__libcpp_is_constant_evaluated() && __builtin_constant_p(__l.__is_long_)) {
+ return __l.__is_long_;
+ }
+ return __s.__is_long_;
+ }
+
+ // If __long is not trivially constructible (this occurs if the pointer type inside __long is not trivially
+ // constructible) then the lifetime of __l must be explicitly started with std::__construct_at before it can be
+ // assigned to. If __long is trivially constructible, then the lifetime of __l can be implicitly started with a
+ // simple assignment and therefore does not need to be explicitly initialised here.
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __begin_long_lifetime() {
+# if _LIBCPP_STD_VER >= 17
+ if constexpr (!__is_trivially_constructible(__long_t)) {
+# else
+ if (!__is_trivially_constructible(__long_t)) {
+# endif // _LIBCPP_STD_VER >= 17
+ std::__construct_at(std::addressof(__l));
+ }
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t() : __s() {}
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__short __r) : __s(__r) {}
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__long_t __r) : __l(__r) {}
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__uninitialized_tag) : __s() {}
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(const __rep_t& __other) {
+ if (__other.__is_long()) {
+ __begin_long_lifetime();
+ __l = __other.__l;
+ } else
+ __s = __other.__s;
+ }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t(__rep_t&& __other) {
+ if (__other.__is_long()) {
+ __begin_long_lifetime();
+ __l = std::move(__other.__l);
+ } else
+ __s = __other.__s;
+ }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t& operator=(const __rep_t& __other) {
+ if (__is_long())
+ __l.~__long_t();
+ if (__other.__is_long()) {
+ __begin_long_lifetime();
+ __l = __other.__l;
+ } else
+ __s = __other.__s;
+ return *this;
+ }
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 __rep_t& operator=(__rep_t&& __other) {
+ if (__is_long())
+ __l.~__long_t();
+ if (__other.__is_long()) {
+ __begin_long_lifetime();
+ __l = std::move(__other.__l);
+ } else
+ __s = __other.__s;
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~__rep_t() {
+ if (__is_long())
+ __l.~__long_t();
+ }
};
+ using __rep = __rep_t<__long>;
+
_LIBCPP_COMPRESSED_PAIR(__rep, __rep_, allocator_type, __alloc_);
// annotate the string with its size() at scope exit. The string has to be in a valid state at that point.
@@ -1182,6 +1263,8 @@ public:
# ifndef _LIBCPP_CXX03_LANG
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 basic_string&
operator=(basic_string&& __str) noexcept(__noexcept_move_assign_container<_Allocator, __alloc_traits>::value) {
+ if (this == std::addressof(__str))
+ return *this;
__move_assign(__str, integral_constant<bool, __alloc_traits::propagate_on_container_move_assignment::value>());
return *this;
}
@@ -2254,6 +2337,7 @@ private:
__annotate_new(__size);
return __get_short_pointer();
} else {
+ __rep_.__begin_long_lifetime();
__rep_.__l = __allocate_long_buffer(__alloc_, __size);
__annotate_new(__size);
return __get_long_pointer();
@@ -2884,31 +2968,10 @@ basic_string<_CharT, _Traits, _Allocator>::__move_assign(basic_string& __str, tr
}
# endif
}
- size_type __str_old_size = __str.size();
- bool __str_was_short = !__str.__is_long();
__move_assign_alloc(__str);
__rep_ = __str.__rep_;
- __str.__set_short_size(0);
- traits_type::assign(__str.__get_short_pointer()[0], value_type());
-
- if (__str_was_short && this != std::addressof(__str))
- __str.__annotate_shrink(__str_old_size);
- else
- // ASan annotations: was long, so object memory is unpoisoned as new.
- // Or is same as *this, and __annotate_delete() was called.
- __str.__annotate_new(0);
-
- // ASan annotations: Guard against `std::string s; s = std::move(s);`
- // You can find more here: https://en.cppreference.com/w/cpp/utility/move
- // Quote: "Unless otherwise specified, all standard library objects that have been moved
- // from are placed in a "valid but unspecified state", meaning the object's class
- // invariants hold (so functions without preconditions, such as the assignment operator,
- // can be safely used on the object after it was moved from):"
- // Quote: "v = std::move(v); // the value of v is unspecified"
- if (!__is_long() && std::addressof(__str) != this)
- // If it is long string, delete was never called on original __str's buffer.
- __annotate_new(__get_short_size());
+ __str.__rep_ = __rep();
}
# endif
@@ -3385,6 +3448,7 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20 void basic_string<_CharT, _Traits, _Allocat
// We're a long string and we're shrinking into the small buffer.
if (__fits_in_sso(__size)) {
__annotation_guard __g(*this);
+ __rep_ = __rep();
__set_short_size(__size);
traits_type::copy(std::__to_address(__get_short_pointer()), std::__to_address(__ptr), __size + 1);
__alloc_traits::deallocate(__alloc_, __ptr, __cap);
diff --git a/libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp b/libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp
index 49cbde14c568b..f78a7ab35326b 100644
--- a/libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/alignof.compile.pass.cpp
@@ -54,6 +54,9 @@ class small_iter_allocator {
template <class CharT>
using min_string = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT>>;
+template <class CharT>
+using fancy_string = std::basic_string<CharT, std::char_traits<CharT>, fancy_pointer_allocator<CharT>>;
+
template <class CharT>
using test_string = std::basic_string<CharT, std::char_traits<CharT>, test_allocator<CharT>>;
@@ -64,6 +67,7 @@ using small_string = std::basic_string<CharT, std::char_traits<CharT>, small_ite
static_assert(alignof(std::string) == 8, "");
static_assert(alignof(min_string<char>) == 8, "");
+static_assert(alignof(fancy_string<char>) == 8, "");
static_assert(alignof(test_string<char>) == 8, "");
static_assert(alignof(small_string<char>) == 2, "");
@@ -71,11 +75,13 @@ static_assert(alignof(small_string<char>) == 2, "");
# if __WCHAR_WIDTH__ == 32
static_assert(alignof(std::wstring) == 8, "");
static_assert(alignof(min_string<wchar_t>) == 8, "");
+static_assert(alignof(fancy_string<wchar_t>) == 8, "");
static_assert(alignof(test_string<wchar_t>) == 8, "");
static_assert(alignof(small_string<wchar_t>) == 4, "");
# elif __WCHAR_WIDTH__ == 16
static_assert(alignof(std::wstring) == 8, "");
static_assert(alignof(min_string<wchar_t>) == 8, "");
+static_assert(alignof(fancy_string<wchar_t>) == 8, "");
static_assert(alignof(test_string<wchar_t>) == 8, "");
static_assert(alignof(small_string<wchar_t>) == 2, "");
# else
@@ -86,6 +92,7 @@ static_assert(alignof(small_string<wchar_t>) == 2, "");
# ifndef TEST_HAS_NO_CHAR8_T
static_assert(alignof(std::u8string) == 8, "");
static_assert(alignof(min_string<char8_t>) == 8, "");
+static_assert(alignof(fancy_string<char8_t>) == 8, "");
static_assert(alignof(test_string<char8_t>) == 8, "");
static_assert(alignof(small_string<char8_t>) == 2, "");
# endif
@@ -95,6 +102,8 @@ static_assert(alignof(std::u16string) == 8, "");
static_assert(alignof(std::u32string) == 8, "");
static_assert(alignof(min_string<char16_t>) == 8, "");
static_assert(alignof(min_string<char32_t>) == 8, "");
+static_assert(alignof(fancy_string<char16_t>) == 8, "");
+static_assert(alignof(fancy_string<char32_t>) == 8, "");
static_assert(alignof(test_string<char16_t>) == 8, "");
static_assert(alignof(test_string<char32_t>) == 8, "");
static_assert(alignof(small_string<char16_t>) == 2, "");
@@ -105,6 +114,7 @@ static_assert(alignof(small_string<char32_t>) == 4, "");
static_assert(alignof(std::string) == 4, "");
static_assert(alignof(min_string<char>) == 4, "");
+static_assert(alignof(fancy_string<char>) == 4, "");
static_assert(alignof(test_string<char>) == 4, "");
static_assert(alignof(small_string<char>) == 2, "");
@@ -112,11 +122,13 @@ static_assert(alignof(small_string<char>) == 2, "");
# if __WCHAR_WIDTH__ == 32
static_assert(alignof(std::wstring) == 4, "");
static_assert(alignof(min_string<wchar_t>) == 4, "");
+static_assert(alignof(fancy_string<wchar_t>) == 4, "");
static_assert(alignof(test_string<wchar_t>) == 4, "");
static_assert(alignof(small_string<wchar_t>) == 4, "");
# elif __WCHAR_WIDTH__ == 16
static_assert(alignof(std::wstring) == 4, "");
static_assert(alignof(min_string<wchar_t>) == 4, "");
+static_assert(alignof(fancy_string<wchar_t>) == 4, "");
static_assert(alignof(test_string<wchar_t>) == 4, "");
static_assert(alignof(small_string<wchar_t>) == 2, "");
# else
@@ -127,6 +139,7 @@ static_assert(alignof(small_string<wchar_t>) == 2, "");
# ifndef TEST_HAS_NO_CHAR8_T
static_assert(alignof(std::u8string) == 4, "");
static_assert(alignof(min_string<char8_t>) == 4, "");
+static_assert(alignof(fancy_string<char8_t>) == 4, "");
static_assert(alignof(test_string<char8_t>) == 4, "");
static_assert(alignof(small_string<char8_t>) == 2, "");
# endif
@@ -136,6 +149,8 @@ static_assert(alignof(std::u16string) == 4, "");
static_assert(alignof(std::u32string) == 4, "");
static_assert(alignof(min_string<char16_t>) == 4, "");
static_assert(alignof(min_string<char32_t>) == 4, "");
+static_assert(alignof(fancy_string<char16_t>) == 4, "");
+static_assert(alignof(fancy_string<char32_t>) == 4, "");
static_assert(alignof(test_string<char16_t>) == 4, "");
static_assert(alignof(test_string<char32_t>) == 4, "");
static_assert(alignof(small_string<char32_t>) == 4, "");
diff --git a/libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp b/libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp
index 31f1a94c73216..aeb07649d7bf9 100644
--- a/libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/sizeof.compile.pass.cpp
@@ -52,6 +52,9 @@ class small_iter_allocator {
template <class CharT>
using min_string = std::basic_string<CharT, std::char_traits<CharT>, min_allocator<CharT> >;
+template <class CharT>
+using fancy_string = std::basic_string<CharT, std::char_traits<CharT>, fancy_pointer_allocator<CharT> >;
+
template <class CharT>
using test_string = std::basic_string<CharT, std::char_traits<CharT>, test_allocator<CharT> >;
@@ -62,6 +65,9 @@ using small_string = std::basic_string<CharT, std::char_traits<CharT>, small_ite
static_assert(sizeof(std::string) == 24, "");
static_assert(sizeof(min_string<char>) == 24, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char>) == 40, "");
+# endif
static_assert(sizeof(test_string<char>) == 32, "");
static_assert(sizeof(small_string<char>) == 6, "");
@@ -69,11 +75,17 @@ static_assert(sizeof(small_string<char>) == 6, "");
# if __WCHAR_WIDTH__ == 32
static_assert(sizeof(std::wstring) == 24, "");
static_assert(sizeof(min_string<wchar_t>) == 24, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<wchar_t>) == 40, "");
+# endif
static_assert(sizeof(test_string<wchar_t>) == 32, "");
static_assert(sizeof(small_string<wchar_t>) == 12, "");
# elif __WCHAR_WIDTH__ == 16
static_assert(sizeof(std::wstring) == 24, "");
static_assert(sizeof(min_string<wchar_t>) == 24, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<wchar_t>) == 40, "");
+# endif
static_assert(sizeof(test_string<wchar_t>) == 32, "");
static_assert(sizeof(small_string<wchar_t>) == 6, "");
# else
@@ -84,6 +96,9 @@ static_assert(sizeof(small_string<wchar_t>) == 6, "");
# ifndef TEST_HAS_NO_CHAR8_T
static_assert(sizeof(std::u8string) == 24, "");
static_assert(sizeof(min_string<char8_t>) == 24, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char8_t>) == 40, "");
+# endif
static_assert(sizeof(test_string<char8_t>) == 32, "");
static_assert(sizeof(small_string<char8_t>) == 6, "");
# endif
@@ -93,6 +108,10 @@ static_assert(sizeof(std::u16string) == 24, "");
static_assert(sizeof(std::u32string) == 24, "");
static_assert(sizeof(min_string<char16_t>) == 24, "");
static_assert(sizeof(min_string<char32_t>) == 24, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char16_t>) == 40, "");
+static_assert(sizeof(fancy_string<char32_t>) == 40, "");
+# endif
static_assert(sizeof(test_string<char16_t>) == 32, "");
static_assert(sizeof(test_string<char32_t>) == 32, "");
static_assert(sizeof(small_string<char16_t>) == 6, "");
@@ -103,6 +122,9 @@ static_assert(sizeof(small_string<char32_t>) == 12, "");
static_assert(sizeof(std::string) == 12, "");
static_assert(sizeof(min_string<char>) == 12, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char>) == 20, "");
+# endif
static_assert(sizeof(test_string<char>) == 24, "");
static_assert(sizeof(small_string<char>) == 6, "");
@@ -110,11 +132,17 @@ static_assert(sizeof(small_string<char>) == 6, "");
# if __WCHAR_WIDTH__ == 32
static_assert(sizeof(std::wstring) == 12, "");
static_assert(sizeof(min_string<wchar_t>) == 12, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<wchar_t>) == 20, "");
+# endif
static_assert(sizeof(test_string<wchar_t>) == 24, "");
static_assert(sizeof(small_string<wchar_t>) == 12, "");
# elif __WCHAR_WIDTH__ == 16
static_assert(sizeof(std::wstring) == 12, "");
static_assert(sizeof(min_string<wchar_t>) == 12, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<wchar_t>) == 20, "");
+# endif
static_assert(sizeof(test_string<wchar_t>) == 24, "");
static_assert(sizeof(small_string<wchar_t>) == 6, "");
# else
@@ -125,6 +153,9 @@ static_assert(sizeof(small_string<wchar_t>) == 6, "");
# ifndef TEST_HAS_NO_CHAR8_T
static_assert(sizeof(std::u8string) == 12, "");
static_assert(sizeof(min_string<char8_t>) == 12, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char8_t>) == 20, "");
+# endif
static_assert(sizeof(test_string<char8_t>) == 24, "");
static_assert(sizeof(small_string<char>) == 6, "");
# endif
@@ -134,6 +165,10 @@ static_assert(sizeof(std::u16string) == 12, "");
static_assert(sizeof(std::u32string) == 12, "");
static_assert(sizeof(min_string<char16_t>) == 12, "");
static_assert(sizeof(min_string<char32_t>) == 12, "");
+# if TEST_STD_VER >= 11
+static_assert(sizeof(fancy_string<char16_t>) == 20, "");
+static_assert(sizeof(fancy_string<char32_t>) == 20, "");
+# endif
static_assert(sizeof(test_string<char16_t>) == 24, "");
static_assert(sizeof(test_string<char32_t>) == 24, "");
static_assert(sizeof(small_string<char16_t>) == 6, "");
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp
index 36a485a1e4d00..975414643b73b 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.back.pass.cpp
@@ -19,6 +19,7 @@
#include "check_assertion.h"
#include "min_allocator.h"
+#include "test_allocator.h"
template <class S>
void test() {
@@ -29,6 +30,7 @@ void test() {
int main(int, char**) {
test<std::string>();
test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+ test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
return 0;
}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp
index d810acd67e7e7..40496f3b7c57d 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cback.pass.cpp
@@ -19,6 +19,7 @@
#include "check_assertion.h"
#include "min_allocator.h"
+#include "test_allocator.h"
template <class S>
void test() {
@@ -29,6 +30,7 @@ void test() {
int main(int, char**) {
test<std::string>();
test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+ test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
return 0;
}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp
index 12e7ef3328b04..0fa0d2ffe29f5 100644
--- a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp
+++ b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cfront.pass.cpp
@@ -19,6 +19,7 @@
#include "check_assertion.h"
#include "min_allocator.h"
+#include "test_allocator.h"
template <class S>
void test() {
@@ -29,6 +30,7 @@ void test() {
int main(int, char**) {
test<std::string>();
test<std::basic_string<char, std::char_traits<char>, min_allocator<char> > >();
+ test<std::basic_string<char, std::char_traits<char>, fancy_pointer_allocator<char> > >();
return 0;
}
diff --git a/libcxx/test/libcxx/strings/basic.string/string.access/assert.cindex.pass.cpp b/libcxx/test/libcxx/strings/basic.string/string.access/assert.cindex.pass.cpp
i...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/199264
More information about the libcxx-commits
mailing list