[libcxx-commits] [libcxx] [libc++] Fix ambiguous calls to std::min in basic_string (PR #132402)
Peng Liu via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Mar 21 07:49:32 PDT 2025
https://github.com/winner245 updated https://github.com/llvm/llvm-project/pull/132402
>From 9d1967a556dc74c3304f4318b688048eba0d4199 Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Sat, 15 Feb 2025 12:48:25 -0500
Subject: [PATCH] Fix ambiguous calls to std::min in basic_string
---
libcxx/include/string | 28 +-
.../min_call_varying_size_types.pass.cpp | 246 ++++++++++++++++++
2 files changed, 260 insertions(+), 14 deletions(-)
create mode 100644 libcxx/test/std/strings/basic.string/min_call_varying_size_types.pass.cpp
diff --git a/libcxx/include/string b/libcxx/include/string
index fa87dc2fddb59..0885362ca7e22 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1101,7 +1101,7 @@ public:
size_type __str_sz = __str.size();
if (__pos > __str_sz)
this->__throw_out_of_range();
- __init(__str.data() + __pos, std::min(__n, __str_sz - __pos));
+ __init(__str.data() + __pos, std::min<size_type>(__n, __str_sz - __pos));
}
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
@@ -2943,7 +2943,7 @@ basic_string<_CharT, _Traits, _Allocator>::assign(const basic_string& __str, siz
size_type __sz = __str.size();
if (__pos > __sz)
this->__throw_out_of_range();
- return assign(__str.data() + __pos, std::min(__n, __sz - __pos));
+ return assign(__str.data() + __pos, std::min<size_type>(__n, __sz - __pos));
}
template <class _CharT, class _Traits, class _Allocator>
@@ -2957,7 +2957,7 @@ basic_string<_CharT, _Traits, _Allocator>::assign(const _Tp& __t, size_type __po
size_type __sz = __sv.size();
if (__pos > __sz)
this->__throw_out_of_range();
- return assign(__sv.data() + __pos, std::min(__n, __sz - __pos));
+ return assign(__sv.data() + __pos, std::min<size_type>(__n, __sz - __pos));
}
template <class _CharT, class _Traits, class _Allocator>
@@ -3088,7 +3088,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(const basic_string& __str, siz
size_type __sz = __str.size();
if (__pos > __sz)
this->__throw_out_of_range();
- return append(__str.data() + __pos, std::min(__n, __sz - __pos));
+ return append(__str.data() + __pos, std::min<size_type>(__n, __sz - __pos));
}
template <class _CharT, class _Traits, class _Allocator>
@@ -3102,7 +3102,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(const _Tp& __t, size_type __po
size_type __sz = __sv.size();
if (__pos > __sz)
this->__throw_out_of_range();
- return append(__sv.data() + __pos, std::min(__n, __sz - __pos));
+ return append(__sv.data() + __pos, std::min<size_type>(__n, __sz - __pos));
}
template <class _CharT, class _Traits, class _Allocator>
@@ -3210,7 +3210,7 @@ basic_string<_CharT, _Traits, _Allocator>::insert(
size_type __str_sz = __str.size();
if (__pos2 > __str_sz)
this->__throw_out_of_range();
- return insert(__pos1, __str.data() + __pos2, std::min(__n, __str_sz - __pos2));
+ return insert(__pos1, __str.data() + __pos2, std::min<size_type>(__n, __str_sz - __pos2));
}
template <class _CharT, class _Traits, class _Allocator>
@@ -3224,7 +3224,7 @@ basic_string<_CharT, _Traits, _Allocator>::insert(size_type __pos1, const _Tp& _
size_type __str_sz = __sv.size();
if (__pos2 > __str_sz)
this->__throw_out_of_range();
- return insert(__pos1, __sv.data() + __pos2, std::min(__n, __str_sz - __pos2));
+ return insert(__pos1, __sv.data() + __pos2, std::min<size_type>(__n, __str_sz - __pos2));
}
template <class _CharT, class _Traits, class _Allocator>
@@ -3268,7 +3268,7 @@ basic_string<_CharT, _Traits, _Allocator>::replace(
size_type __sz = size();
if (__pos > __sz)
this->__throw_out_of_range();
- __n1 = std::min(__n1, __sz - __pos);
+ __n1 = std::min<size_type>(__n1, __sz - __pos);
size_type __cap = capacity();
if (__cap - __sz + __n1 >= __n2) {
value_type* __p = std::__to_address(__get_pointer());
@@ -3310,7 +3310,7 @@ basic_string<_CharT, _Traits, _Allocator>::replace(size_type __pos, size_type __
size_type __sz = size();
if (__pos > __sz)
this->__throw_out_of_range();
- __n1 = std::min(__n1, __sz - __pos);
+ __n1 = std::min<size_type>(__n1, __sz - __pos);
size_type __cap = capacity();
value_type* __p;
if (__cap - __sz + __n1 >= __n2) {
@@ -3346,7 +3346,7 @@ basic_string<_CharT, _Traits, _Allocator>::replace(
size_type __str_sz = __str.size();
if (__pos2 > __str_sz)
this->__throw_out_of_range();
- return replace(__pos1, __n1, __str.data() + __pos2, std::min(__n2, __str_sz - __pos2));
+ return replace(__pos1, __n1, __str.data() + __pos2, std::min<size_type>(__n2, __str_sz - __pos2));
}
template <class _CharT, class _Traits, class _Allocator>
@@ -3361,7 +3361,7 @@ basic_string<_CharT, _Traits, _Allocator>::replace(
size_type __str_sz = __sv.size();
if (__pos2 > __str_sz)
this->__throw_out_of_range();
- return replace(__pos1, __n1, __sv.data() + __pos2, std::min(__n2, __str_sz - __pos2));
+ return replace(__pos1, __n1, __sv.data() + __pos2, std::min<size_type>(__n2, __str_sz - __pos2));
}
template <class _CharT, class _Traits, class _Allocator>
@@ -3381,7 +3381,7 @@ basic_string<_CharT, _Traits, _Allocator>::__erase_external_with_move(size_type
if (__n) {
size_type __sz = size();
value_type* __p = std::__to_address(__get_pointer());
- __n = std::min(__n, __sz - __pos);
+ __n = std::min<size_type>(__n, __sz - __pos);
size_type __n_move = __sz - __pos - __n;
if (__n_move != 0)
traits_type::move(__p + __pos, __p + __pos + __n, __n_move);
@@ -3555,7 +3555,7 @@ basic_string<_CharT, _Traits, _Allocator>::copy(value_type* __s, size_type __n,
size_type __sz = size();
if (__pos > __sz)
this->__throw_out_of_range();
- size_type __rlen = std::min(__n, __sz - __pos);
+ size_type __rlen = std::min<size_type>(__n, __sz - __pos);
traits_type::copy(__s, data() + __pos, __rlen);
return __rlen;
}
@@ -3609,7 +3609,7 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20 int basic_string<_CharT, _Traits, _Allocato
size_type __sz = size();
if (__pos1 > __sz || __n2 == npos)
this->__throw_out_of_range();
- size_type __rlen = std::min(__n1, __sz - __pos1);
+ size_type __rlen = std::min<size_type>(__n1, __sz - __pos1);
int __r = traits_type::compare(data() + __pos1, __s, std::min(__rlen, __n2));
if (__r == 0) {
if (__rlen < __n2)
diff --git a/libcxx/test/std/strings/basic.string/min_call_varying_size_types.pass.cpp b/libcxx/test/std/strings/basic.string/min_call_varying_size_types.pass.cpp
new file mode 100644
index 0000000000000..7128bc0313e51
--- /dev/null
+++ b/libcxx/test/std/strings/basic.string/min_call_varying_size_types.pass.cpp
@@ -0,0 +1,246 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <string>
+
+// XFAIL: FROZEN-CXX03-HEADERS-FIXME
+
+// Make sure basic_string constructors and functions operate properly for allocators with small `size_type`s.
+// Related issue: https://github.com/llvm/llvm-project/issues/125187
+
+// constexpr basic_string(
+// basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator()); // C++23
+// basic_string(const basic_string& str, size_type pos, size_type n,
+// const Allocator& a = Allocator()); // constexpr since C++20
+// basic_string& assign(const basic_string& str, size_type pos, size_type n=npos); // constexpr since C++20
+// template <class T>
+// basic_string& assign(const T& t, size_type pos, size_type n=npos); // C++17, constexpr since C++20
+// basic_string& append(const basic_string& str, size_type pos, size_type n=npos); // constexpr since C++20
+// template <class T>
+// basic_string& append(const T& t, size_type pos, size_type n=npos); // C++17, constexpr since C++20
+// basic_string& insert(size_type pos1, const basic_string& str,
+// size_type pos2, size_type n=npos); // constexpr since C++20
+// template <class T>
+// basic_string& insert(size_type pos1, const T& t, size_type pos2, size_type n=npos); // C++17, constexpr since C++20
+// basic_string& erase(size_type pos = 0, size_type n = npos); // constexpr since C++20
+// basic_string& replace(size_type pos, size_type n1, const value_type* s, size_type n2); // constexpr since C++20
+// basic_string& replace(size_type pos, size_type n1, size_type n2, value_type c); // constexpr since C++20
+// basic_string& replace(size_type pos1, size_type n1, const basic_string& str,
+// size_type pos2, size_type n2=npos); // constexpr since C++20
+// template <class T>
+// basic_string& replace(size_type pos1, size_type n1, const T& t,
+// size_type pos2, size_type n2= npos); // C++17, constexpr since C++20
+// size_type copy(value_type* s, size_type n, size_type pos = 0) const; // constexpr since C++20
+// template <class T>
+// int compare(const T& t) const noexcept; // C++17, constexpr since C++20
+// int compare(size_type pos1, size_type n1, const value_type* s, size_type n2) const; // constexpr since C++20
+
+#include <cassert>
+#include <cstdint>
+#include <string>
+#include <string_view>
+
+#include "sized_allocator.h"
+#include "test_macros.h"
+
+template <class SizeT, class DiffT, class CharT = char, class Traits = std::char_traits<CharT> >
+TEST_CONSTEXPR_CXX20 void test_with_custom_size_type() {
+ using Alloc = sized_allocator<CharT, SizeT, DiffT>;
+ using string = std::basic_string<CharT, Traits, Alloc>;
+ string s = "hello world";
+
+ // The following tests validate all possible calls to std::min within <basic_string>
+ { // basic_string(const basic_string& str, size_type pos, size_type n, const Allocator& a = Allocator())
+ assert(string(s, 0, 5) == "hello");
+ assert(string(s, 0, 5, Alloc(3)) == "hello");
+ assert(string(s, 6, 5) == "world");
+ assert(string(s, 6, 5, Alloc(3)) == "world");
+ assert(string(s, 6, 100) == "world");
+ assert(string(s, 6, 100, Alloc(3)) == "world");
+ }
+#if TEST_STD_VER >= 23
+ { // constexpr basic_string(basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator());
+ assert(string(string(s), 0, 5) == "hello");
+ assert(string(string(s), 0, 5, Alloc(3)) == "hello");
+ assert(string(string(s), 6, 5) == "world");
+ assert(string(string(s), 6, 5, Alloc(3)) == "world");
+ assert(string(string(s), 6, 100) == "world");
+ assert(string(string(s), 6, 100, Alloc(3)) == "world");
+ }
+#endif
+ { // basic_string& assign(const basic_string& str, size_type pos, size_type n=npos)
+ string s1 = s;
+ string s2 = "cplusplus";
+ s1.assign(s2, 0, 5);
+ assert(s1 == "cplus");
+ s1.assign(s2, 0, 9);
+ assert(s1 == "cplusplus");
+ s1.assign(s2, 5);
+ assert(s1 == "plus");
+ s1.assign(s2, 0, 100);
+ assert(s1 == "cplusplus");
+ s1.assign(s2, 4);
+ assert(s1 == "splus");
+ s1.assign(s2, 0);
+ assert(s1 == "cplusplus");
+ }
+#if TEST_STD_VER >= 17
+ { // template <class T> basic_string& assign(const T& t, size_type pos, size_type n=npos)
+ std::string_view sv = "cplusplus";
+ string s1 = s;
+ s1.assign(sv, 0, 5);
+ assert(s1 == "cplus");
+ s1.assign(sv, 0, 9);
+ assert(s1 == "cplusplus");
+ s1.assign(sv, 5);
+ assert(s1 == "plus");
+ s1.assign(sv, 0, 100);
+ assert(s1 == "cplusplus");
+ s1.assign(sv, 4);
+ assert(s1 == "splus");
+ s1.assign(sv, 0);
+ assert(s1 == "cplusplus");
+ }
+#endif
+ { // basic_string& append(const basic_string& str, size_type pos, size_type n=npos)
+ string s1 = s;
+ string s2 = " of cplusplus";
+ s1.append(s2, 0, 5);
+ assert(s1 == "hello world of c");
+ s1 = s;
+ s1.append(s2, 0, 100);
+ assert(s1 == "hello world of cplusplus");
+ s1 = s;
+ s1.append(s2, 0);
+ assert(s1 == "hello world of cplusplus");
+ }
+#if TEST_STD_VER >= 17
+ { // template <class T> basic_string& append(const T& t, size_type pos, size_type n=npos)
+ string s1 = s;
+ std::string_view sv = " of cplusplus";
+ s1.append(sv, 0, 5);
+ assert(s1 == "hello world of c");
+ s1 = s;
+ s1.append(sv, 0, 100);
+ assert(s1 == "hello world of cplusplus");
+ s1 = s;
+ s1.append(sv, 0);
+ assert(s1 == "hello world of cplusplus");
+ }
+#endif
+ { // basic_string& insert(size_type pos1, const basic_string& str, size_type pos2, size_type n=npos)
+ string s1 = s;
+ string s2 = " cplusplus";
+ s1.insert(5, s2, 0, 2);
+ assert(s1 == "hello c world");
+ s1 = s;
+ s1.insert(5, s2, 0, 100);
+ assert(s1 == "hello cplusplus world");
+ s1 = s;
+ s1.insert(5, s2, 0);
+ assert(s1 == "hello cplusplus world");
+ }
+#if TEST_STD_VER >= 17
+ { // template <class T> basic_string& insert(size_type pos1, const T& t, size_type pos2, size_type n=npos)
+ string s1 = s;
+ std::string_view sv = " cplusplus";
+ s1.insert(5, sv, 0, 2);
+ assert(s1 == "hello c world");
+ s1 = s;
+ s1.insert(5, sv, 0, 100);
+ assert(s1 == "hello cplusplus world");
+ s1 = s;
+ s1.insert(5, sv, 0);
+ assert(s1 == "hello cplusplus world");
+ }
+#endif
+ { // basic_string& erase(size_type pos = 0, size_type n = npos)
+ string s1 = s;
+ assert(s1.erase(5, 100) == "hello");
+ s1 = s;
+ assert(s1.erase(5) == "hello");
+ assert(s1.erase().empty());
+ }
+ { // basic_string& replace(size_type pos, size_type n1, const value_type* s, size_type n2)
+ string s1 = s;
+ const char* s2 = "cpluscplus";
+ assert(s1.replace(6, 5, s2, 1) == "hello c");
+ assert(s1.replace(6, 1, s2, 10) == "hello cpluscplus");
+ }
+ { // basic_string& replace(size_type pos, size_type n1, size_type n2, value_type c)
+ string s1 = s;
+ assert(s1.replace(5, 6, 2, 'o') == "hellooo");
+ assert(s1.replace(5, 2, 0, 'o') == "hello");
+ }
+ { // basic_string& replace(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2=npos)
+ string s1 = s;
+ string s2 = "cplusplus";
+ assert(s1.replace(6, 5, s2, 0, 1) == "hello c");
+ assert(s1.replace(7, 0, s2, 1, 9) == "hello cplusplus");
+ s1 = s;
+ assert(s1.replace(6, 5, s2, 0, 100) == "hello cplusplus");
+ s1 = s;
+ assert(s1.replace(6, 5, s2, 0) == "hello cplusplus");
+ }
+#if TEST_STD_VER >= 17
+ { // template <class T> basic_string& replace(size_type pos1, size_type n1, const T& t, size_type pos2, size_type n2= npos)
+ string s1 = s;
+ std::string_view sv = "cplusplus";
+ assert(s1.replace(6, 5, sv, 0, 1) == "hello c");
+ assert(s1.replace(7, 0, sv, 1, 9) == "hello cplusplus");
+ s1 = s;
+ assert(s1.replace(6, 5, sv, 0, 100) == "hello cplusplus");
+ s1 = s;
+ assert(s1.replace(6, 5, sv, 0) == "hello cplusplus");
+ }
+#endif
+ { // size_type copy(value_type* s, size_type n, size_type pos = 0) const
+ string s1 = s;
+ char bar[100] = {};
+ s1.copy(bar, s1.size());
+ assert(s1 == bar);
+ }
+ { // int compare(size_type pos1, size_type n1, const value_type* s, size_type n2) const
+ string s1 = s;
+ assert(s1.compare(0, 11, "hello world", 11) == 0);
+ assert(s1.compare(0, 11, "hello", 11) > 0);
+ assert(s1.compare(0, 11, "hello world C++", 100) < 0);
+ }
+#if TEST_STD_VER >= 17
+ { // template <class T> int compare(const T& t) const noexcept;
+ using std::operator""sv;
+ string s1 = s;
+ assert(s1.compare("hello world"sv) == 0);
+ assert(s1.compare("hello"sv) > 0);
+ assert(s1.compare("hello world C++"sv) < 0);
+ }
+#endif
+}
+
+TEST_CONSTEXPR_CXX20 bool test() {
+ test_with_custom_size_type<unsigned char, signed char>();
+ test_with_custom_size_type<unsigned short, short>();
+ test_with_custom_size_type<unsigned short, short>();
+ test_with_custom_size_type<unsigned, int>();
+ test_with_custom_size_type<std::size_t, std::ptrdiff_t>();
+ test_with_custom_size_type<std::uint8_t, std::int8_t>();
+ test_with_custom_size_type<std::uint16_t, std::int16_t>();
+ test_with_custom_size_type<std::uint32_t, std::int32_t>();
+ test_with_custom_size_type<std::uint64_t, std::int64_t>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER > 17
+ static_assert(test());
+#endif
+
+ return 0;
+}
More information about the libcxx-commits
mailing list