[libcxx-commits] [libcxx] [libcxx][string] Test: default constructed allocators can be unequal (PR #195839)
Nikita Belenkiy via libcxx-commits
libcxx-commits at lists.llvm.org
Tue May 5 04:58:02 PDT 2026
https://github.com/kitsnet created https://github.com/llvm/llvm-project/pull/195839
In preparation to fancy_pointer_allocator, where multiple default constructed instances of the allocator may have unrelated internal states, the string tests were changed to not always assume that get_alocator() with default allocator returns value equal to Allocator().
In addition, one missing test path where substring move will cause allocation due to incompatible allocators (as already implemented in basic_string) was found and addressed.
>From 9bda04f2809a83b86c542eb7f1f7ec4a86b92704 Mon Sep 17 00:00:00 2001
From: Nikita Belenkiy <nikita.nb.belenkiy at partner.bmw.de>
Date: Tue, 5 May 2026 13:09:09 +0200
Subject: [PATCH] [libcxx][string] Test: default constructed allocators can be
unequal
In preparation to fancy_pointer_allocator, where multiple default constructed
instances of the allocator may have unrelated internal states, the string tests
were changed to not always assume that get_alocator() with default allocator
returns value equal to Allocator().
In addition, one missing test path where substring move will cause allocation
due to incompatible allocators (as already implemented in basic_string) was found
and addressed.
---
.../basic.string/string.cons/T_size_size.pass.cpp | 3 +--
.../strings/basic.string/string.cons/alloc.pass.cpp | 10 +++++++---
.../basic.string/string.cons/iter_alloc.pass.cpp | 2 +-
.../basic.string/string.cons/pointer_alloc.pass.cpp | 2 +-
.../string.cons/pointer_size_alloc.pass.cpp | 2 +-
.../string.cons/size_char_alloc.pass.cpp | 4 ++--
.../basic.string/string.cons/string_view.pass.cpp | 4 ++--
.../strings/basic.string/string.cons/substr.pass.cpp | 6 ++----
.../basic.string/string.cons/substr_rvalue.pass.cpp | 12 ++++++++----
.../string.accessors/get_allocator.pass.cpp | 4 +++-
libcxx/test/support/test_allocator.h | 12 ++++++++++++
11 files changed, 40 insertions(+), 21 deletions(-)
diff --git a/libcxx/test/std/strings/basic.string/string.cons/T_size_size.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/T_size_size.pass.cpp
index dcf697bed752f..8511250812d6a 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/T_size_size.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/T_size_size.pass.cpp
@@ -28,7 +28,6 @@
template <class S, class SV>
TEST_CONSTEXPR_CXX20 void test(SV sv, std::size_t pos, std::size_t n) {
typedef typename S::traits_type T;
- typedef typename S::allocator_type A;
typedef typename S::size_type Size;
if (pos <= sv.size()) {
S s2(sv, static_cast<Size>(pos), static_cast<Size>(n));
@@ -37,7 +36,7 @@ TEST_CONSTEXPR_CXX20 void test(SV sv, std::size_t pos, std::size_t n) {
std::size_t rlen = std::min(sv.size() - pos, n);
assert(s2.size() == rlen);
assert(T::compare(s2.data(), sv.data() + pos, rlen) == 0);
- assert(s2.get_allocator() == A());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
diff --git a/libcxx/test/std/strings/basic.string/string.cons/alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/alloc.pass.cpp
index 91beac37764db..a2b493d5829ff 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/alloc.pass.cpp
@@ -67,7 +67,10 @@ TEST_CONSTEXPR_CXX20 void test2() {
assert(s.data());
assert(s.size() == 0);
assert(s.capacity() >= s.size());
- assert(s.get_allocator() == typename S::allocator_type());
+# if TEST_STD_VER >= 11
+ static_assert(std::is_same<decltype(s.get_allocator()), typename S::allocator_type>::value, "");
+# endif
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s);
LIBCPP_ASSERT(is_string_asan_correct(s));
}
{
@@ -78,12 +81,13 @@ TEST_CONSTEXPR_CXX20 void test2() {
std::is_nothrow_copy_constructible<typename S::allocator_type>::value),
"");
# endif
- S s(typename S::allocator_type{});
+ typename S::allocator_type a{};
+ S s(a);
LIBCPP_ASSERT(s.__invariants());
assert(s.data());
assert(s.size() == 0);
assert(s.capacity() >= s.size());
- assert(s.get_allocator() == typename S::allocator_type());
+ assert(s.get_allocator() == a);
LIBCPP_ASSERT(is_string_asan_correct(s));
}
}
diff --git a/libcxx/test/std/strings/basic.string/string.cons/iter_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/iter_alloc.pass.cpp
index e14227d1a7717..7e924fea39490 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/iter_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/iter_alloc.pass.cpp
@@ -36,7 +36,7 @@ TEST_CONSTEXPR_CXX20 void test(It first, It last) {
++it;
++i;
}
- assert(s2.get_allocator() == Alloc());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
diff --git a/libcxx/test/std/strings/basic.string/string.cons/pointer_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/pointer_alloc.pass.cpp
index d5d5152e11e54..c23afad1c470b 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/pointer_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/pointer_alloc.pass.cpp
@@ -30,7 +30,7 @@ TEST_CONSTEXPR_CXX20 void test(const charT* s) {
LIBCPP_ASSERT(s2.__invariants());
assert(s2.size() == n);
assert(T::compare(s2.data(), s, n) == 0);
- assert(s2.get_allocator() == Alloc());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
diff --git a/libcxx/test/std/strings/basic.string/string.cons/pointer_size_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/pointer_size_alloc.pass.cpp
index b6ba2be46d18b..cf8280e940eed 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/pointer_size_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/pointer_size_alloc.pass.cpp
@@ -28,7 +28,7 @@ TEST_CONSTEXPR_CXX20 void test(const CharT* s, unsigned n) {
LIBCPP_ASSERT(s2.__invariants());
assert(s2.size() == n);
assert(T::compare(s2.data(), s, n) == 0);
- assert(s2.get_allocator() == Alloc());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
diff --git a/libcxx/test/std/strings/basic.string/string.cons/size_char_alloc.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/size_char_alloc.pass.cpp
index a87e01f0fc088..e92232935db3e 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/size_char_alloc.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/size_char_alloc.pass.cpp
@@ -29,7 +29,7 @@ TEST_CONSTEXPR_CXX20 void test(unsigned n, charT c) {
assert(s2.size() == n);
for (unsigned i = 0; i < n; ++i)
assert(s2[i] == c);
- assert(s2.get_allocator() == Alloc());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
@@ -56,7 +56,7 @@ TEST_CONSTEXPR_CXX20 void test(Tp n, Tp c) {
assert(s2.size() == static_cast<std::size_t>(n));
for (int i = 0; i < n; ++i)
assert(s2[i] == c);
- assert(s2.get_allocator() == Alloc());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
}
diff --git a/libcxx/test/std/strings/basic.string/string.cons/string_view.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/string_view.pass.cpp
index ad37b50b83ba7..3ef7fb01f9c2c 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/string_view.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/string_view.pass.cpp
@@ -34,7 +34,7 @@ TEST_CONSTEXPR_CXX20 void test(std::basic_string_view<CharT> sv) {
LIBCPP_ASSERT(s2.__invariants());
assert(s2.size() == sv.size());
assert(T::compare(s2.data(), sv.data(), sv.size()) == 0);
- assert(s2.get_allocator() == Alloc());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
@@ -44,7 +44,7 @@ TEST_CONSTEXPR_CXX20 void test(std::basic_string_view<CharT> sv) {
LIBCPP_ASSERT(s2.__invariants());
assert(s2.size() == sv.size());
assert(T::compare(s2.data(), sv.data(), sv.size()) == 0);
- assert(s2.get_allocator() == Alloc());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
diff --git a/libcxx/test/std/strings/basic.string/string.cons/substr.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/substr.pass.cpp
index cafd9674f4898..a47995699ae34 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/substr.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/substr.pass.cpp
@@ -31,7 +31,6 @@
template <class S>
TEST_CONSTEXPR_CXX20 void test(S str, unsigned pos) {
typedef typename S::traits_type T;
- typedef typename S::allocator_type A;
if (pos <= str.size()) {
S s2(str, pos);
@@ -39,7 +38,7 @@ TEST_CONSTEXPR_CXX20 void test(S str, unsigned pos) {
typename S::size_type rlen = str.size() - pos;
assert(s2.size() == rlen);
assert(T::compare(s2.data(), str.data() + pos, rlen) == 0);
- assert(s2.get_allocator() == A());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
@@ -58,14 +57,13 @@ TEST_CONSTEXPR_CXX20 void test(S str, unsigned pos) {
template <class S>
TEST_CONSTEXPR_CXX20 void test(S str, unsigned pos, unsigned n) {
typedef typename S::traits_type T;
- typedef typename S::allocator_type A;
if (pos <= str.size()) {
S s2(str, pos, n);
LIBCPP_ASSERT(s2.__invariants());
typename S::size_type rlen = std::min<typename S::size_type>(str.size() - pos, n);
assert(s2.size() == rlen);
assert(T::compare(s2.data(), str.data() + pos, rlen) == 0);
- assert(s2.get_allocator() == A());
+ ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(S, s2);
assert(s2.capacity() >= s2.size());
LIBCPP_ASSERT(is_string_asan_correct(s2));
}
diff --git a/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp
index 9c7c341302df9..ae16130d7b5b1 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/substr_rvalue.pass.cpp
@@ -33,12 +33,14 @@ constexpr struct should_throw_exception_t {
template <class S>
constexpr void test_string_pos(S orig, typename S::size_type pos, S expected) {
+ const bool expect_no_allocation = orig.get_allocator() == typename S::allocator_type();
#ifdef _LIBCPP_VERSION
- ConstexprDisableAllocationGuard g;
+ ConstexprDisableAllocationGuard g(expect_no_allocation);
#endif
S substr(std::move(orig), pos);
LIBCPP_ASSERT(orig.__invariants());
- LIBCPP_ASSERT(orig.empty());
+ if (expect_no_allocation)
+ LIBCPP_ASSERT(orig.empty());
LIBCPP_ASSERT(substr.__invariants());
assert(substr == expected);
LIBCPP_ASSERT(is_string_asan_correct(orig));
@@ -93,12 +95,14 @@ constexpr void test_string_pos_alloc(
template <class S>
constexpr void test_string_pos_n(S orig, typename S::size_type pos, typename S::size_type n, S expected) {
+ const bool expect_no_allocation = orig.get_allocator() == typename S::allocator_type();
#ifdef _LIBCPP_VERSION
- ConstexprDisableAllocationGuard g;
+ ConstexprDisableAllocationGuard g(expect_no_allocation);
#endif
S substr(std::move(orig), pos, n);
LIBCPP_ASSERT(orig.__invariants());
- LIBCPP_ASSERT(orig.empty());
+ if (expect_no_allocation)
+ LIBCPP_ASSERT(orig.empty());
LIBCPP_ASSERT(substr.__invariants());
assert(substr == expected);
LIBCPP_ASSERT(is_string_asan_correct(orig));
diff --git a/libcxx/test/std/strings/basic.string/string.ops/string.accessors/get_allocator.pass.cpp b/libcxx/test/std/strings/basic.string/string.ops/string.accessors/get_allocator.pass.cpp
index a6158aeb38dc2..a54df7a1f144c 100644
--- a/libcxx/test/std/strings/basic.string/string.ops/string.accessors/get_allocator.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.ops/string.accessors/get_allocator.pass.cpp
@@ -25,7 +25,9 @@ TEST_CONSTEXPR_CXX20 void test(const S& s, const typename S::allocator_type& a)
template <class Alloc>
TEST_CONSTEXPR_CXX20 void test_string(const Alloc& a) {
using S = std::basic_string<char, std::char_traits<char>, Alloc>;
- test(S(""), Alloc());
+ if (are_default_allocators_always_equal<Alloc>::value)
+ test(S(""), Alloc());
+ test(S("", Alloc(a)), Alloc(a));
test(S("abcde", Alloc(a)), Alloc(a));
test(S("abcdefghij", Alloc(a)), Alloc(a));
test(S("abcdefghijklmnopqrst", Alloc(a)), Alloc(a));
diff --git a/libcxx/test/support/test_allocator.h b/libcxx/test/support/test_allocator.h
index f8b622d7f9520..064aca94a37c2 100644
--- a/libcxx/test/support/test_allocator.h
+++ b/libcxx/test/support/test_allocator.h
@@ -20,6 +20,18 @@
#include "test_macros.h"
+// unless explicitly specified as false
+template <class T>
+struct are_default_allocators_always_equal {
+ enum { value = 1 };
+};
+
+#define ASSERT_CONTAINER_ALLOCATOR_EQUALS_DEFAULT(C, c) \
+ do { \
+ if (are_default_allocators_always_equal<typename C::allocator_type>::value) \
+ assert(c.get_allocator() == typename C::allocator_type()); \
+ } while (0)
+
template <class Alloc>
TEST_CONSTEXPR_CXX20 inline typename std::allocator_traits<Alloc>::size_type alloc_max_size(Alloc const& a) {
typedef std::allocator_traits<Alloc> AT;
More information about the libcxx-commits
mailing list