Skip to content

Commit 9d24186

Browse files
committed
Fix ambiguous calls to std::min in basic_string
1 parent 281028e commit 9d24186

File tree

2 files changed

+259
-15
lines changed

2 files changed

+259
-15
lines changed

libcxx/include/string

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1006,7 +1006,7 @@ public:
10061006
// Turning off ASan instrumentation for variable initialization with _LIBCPP_STRING_INTERNAL_MEMORY_ACCESS
10071007
// does not work consistently during initialization of __r_, so we instead unpoison __str's memory manually first.
10081008
// __str's memory needs to be unpoisoned only in the case where it's a short string.
1009-
: __rep_([](basic_string& __s) -> decltype(__s.__rep_)&& {
1009+
: __rep_([](basic_string& __s) -> decltype(__s.__rep_) && {
10101010
if (!__s.__is_long())
10111011
__s.__annotate_delete();
10121012
return std::move(__s.__rep_);
@@ -1101,7 +1101,7 @@ public:
11011101
size_type __str_sz = __str.size();
11021102
if (__pos > __str_sz)
11031103
this->__throw_out_of_range();
1104-
__init(__str.data() + __pos, std::min(__n, __str_sz - __pos));
1104+
__init(__str.data() + __pos, std::min<size_type>(__n, __str_sz - __pos));
11051105
}
11061106

11071107
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20
@@ -2943,7 +2943,7 @@ basic_string<_CharT, _Traits, _Allocator>::assign(const basic_string& __str, siz
29432943
size_type __sz = __str.size();
29442944
if (__pos > __sz)
29452945
this->__throw_out_of_range();
2946-
return assign(__str.data() + __pos, std::min(__n, __sz - __pos));
2946+
return assign(__str.data() + __pos, std::min<size_type>(__n, __sz - __pos));
29472947
}
29482948

29492949
template <class _CharT, class _Traits, class _Allocator>
@@ -2957,7 +2957,7 @@ basic_string<_CharT, _Traits, _Allocator>::assign(const _Tp& __t, size_type __po
29572957
size_type __sz = __sv.size();
29582958
if (__pos > __sz)
29592959
this->__throw_out_of_range();
2960-
return assign(__sv.data() + __pos, std::min(__n, __sz - __pos));
2960+
return assign(__sv.data() + __pos, std::min<size_type>(__n, __sz - __pos));
29612961
}
29622962

29632963
template <class _CharT, class _Traits, class _Allocator>
@@ -3088,7 +3088,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(const basic_string& __str, siz
30883088
size_type __sz = __str.size();
30893089
if (__pos > __sz)
30903090
this->__throw_out_of_range();
3091-
return append(__str.data() + __pos, std::min(__n, __sz - __pos));
3091+
return append(__str.data() + __pos, std::min<size_type>(__n, __sz - __pos));
30923092
}
30933093

30943094
template <class _CharT, class _Traits, class _Allocator>
@@ -3102,7 +3102,7 @@ basic_string<_CharT, _Traits, _Allocator>::append(const _Tp& __t, size_type __po
31023102
size_type __sz = __sv.size();
31033103
if (__pos > __sz)
31043104
this->__throw_out_of_range();
3105-
return append(__sv.data() + __pos, std::min(__n, __sz - __pos));
3105+
return append(__sv.data() + __pos, std::min<size_type>(__n, __sz - __pos));
31063106
}
31073107

31083108
template <class _CharT, class _Traits, class _Allocator>
@@ -3210,7 +3210,7 @@ basic_string<_CharT, _Traits, _Allocator>::insert(
32103210
size_type __str_sz = __str.size();
32113211
if (__pos2 > __str_sz)
32123212
this->__throw_out_of_range();
3213-
return insert(__pos1, __str.data() + __pos2, std::min(__n, __str_sz - __pos2));
3213+
return insert(__pos1, __str.data() + __pos2, std::min<size_type>(__n, __str_sz - __pos2));
32143214
}
32153215

32163216
template <class _CharT, class _Traits, class _Allocator>
@@ -3224,7 +3224,7 @@ basic_string<_CharT, _Traits, _Allocator>::insert(size_type __pos1, const _Tp& _
32243224
size_type __str_sz = __sv.size();
32253225
if (__pos2 > __str_sz)
32263226
this->__throw_out_of_range();
3227-
return insert(__pos1, __sv.data() + __pos2, std::min(__n, __str_sz - __pos2));
3227+
return insert(__pos1, __sv.data() + __pos2, std::min<size_type>(__n, __str_sz - __pos2));
32283228
}
32293229

32303230
template <class _CharT, class _Traits, class _Allocator>
@@ -3268,7 +3268,7 @@ basic_string<_CharT, _Traits, _Allocator>::replace(
32683268
size_type __sz = size();
32693269
if (__pos > __sz)
32703270
this->__throw_out_of_range();
3271-
__n1 = std::min(__n1, __sz - __pos);
3271+
__n1 = std::min<size_type>(__n1, __sz - __pos);
32723272
size_type __cap = capacity();
32733273
if (__cap - __sz + __n1 >= __n2) {
32743274
value_type* __p = std::__to_address(__get_pointer());
@@ -3310,7 +3310,7 @@ basic_string<_CharT, _Traits, _Allocator>::replace(size_type __pos, size_type __
33103310
size_type __sz = size();
33113311
if (__pos > __sz)
33123312
this->__throw_out_of_range();
3313-
__n1 = std::min(__n1, __sz - __pos);
3313+
__n1 = std::min<size_type>(__n1, __sz - __pos);
33143314
size_type __cap = capacity();
33153315
value_type* __p;
33163316
if (__cap - __sz + __n1 >= __n2) {
@@ -3346,7 +3346,7 @@ basic_string<_CharT, _Traits, _Allocator>::replace(
33463346
size_type __str_sz = __str.size();
33473347
if (__pos2 > __str_sz)
33483348
this->__throw_out_of_range();
3349-
return replace(__pos1, __n1, __str.data() + __pos2, std::min(__n2, __str_sz - __pos2));
3349+
return replace(__pos1, __n1, __str.data() + __pos2, std::min<size_type>(__n2, __str_sz - __pos2));
33503350
}
33513351

33523352
template <class _CharT, class _Traits, class _Allocator>
@@ -3361,7 +3361,7 @@ basic_string<_CharT, _Traits, _Allocator>::replace(
33613361
size_type __str_sz = __sv.size();
33623362
if (__pos2 > __str_sz)
33633363
this->__throw_out_of_range();
3364-
return replace(__pos1, __n1, __sv.data() + __pos2, std::min(__n2, __str_sz - __pos2));
3364+
return replace(__pos1, __n1, __sv.data() + __pos2, std::min<size_type>(__n2, __str_sz - __pos2));
33653365
}
33663366

33673367
template <class _CharT, class _Traits, class _Allocator>
@@ -3381,7 +3381,7 @@ basic_string<_CharT, _Traits, _Allocator>::__erase_external_with_move(size_type
33813381
if (__n) {
33823382
size_type __sz = size();
33833383
value_type* __p = std::__to_address(__get_pointer());
3384-
__n = std::min(__n, __sz - __pos);
3384+
__n = std::min<size_type>(__n, __sz - __pos);
33853385
size_type __n_move = __sz - __pos - __n;
33863386
if (__n_move != 0)
33873387
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,
35553555
size_type __sz = size();
35563556
if (__pos > __sz)
35573557
this->__throw_out_of_range();
3558-
size_type __rlen = std::min(__n, __sz - __pos);
3558+
size_type __rlen = std::min<size_type>(__n, __sz - __pos);
35593559
traits_type::copy(__s, data() + __pos, __rlen);
35603560
return __rlen;
35613561
}
@@ -3609,7 +3609,7 @@ inline _LIBCPP_CONSTEXPR_SINCE_CXX20 int basic_string<_CharT, _Traits, _Allocato
36093609
size_type __sz = size();
36103610
if (__pos1 > __sz || __n2 == npos)
36113611
this->__throw_out_of_range();
3612-
size_type __rlen = std::min(__n1, __sz - __pos1);
3612+
size_type __rlen = std::min<size_type>(__n1, __sz - __pos1);
36133613
int __r = traits_type::compare(data() + __pos1, __s, std::min(__rlen, __n2));
36143614
if (__r == 0) {
36153615
if (__rlen < __n2)
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
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+
// <string>
10+
11+
// Make sure basic_string constructors and functions operate properly for allocators with small `size_type`s.
12+
// Related issue: https://github.com/llvm/llvm-project/issues/125187
13+
14+
// constexpr basic_string(
15+
// basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator()); // C++23
16+
// basic_string(const basic_string& str, size_type pos, size_type n,
17+
// const Allocator& a = Allocator()); // constexpr since C++20
18+
// basic_string& assign(const basic_string& str, size_type pos, size_type n=npos); // constexpr since C++20
19+
// template <class T>
20+
// basic_string& assign(const T& t, size_type pos, size_type n=npos); // C++17, constexpr since C++20
21+
// basic_string& append(const basic_string& str, size_type pos, size_type n=npos); // constexpr since C++20
22+
// template <class T>
23+
// basic_string& append(const T& t, size_type pos, size_type n=npos); // C++17, constexpr since C++20
24+
// basic_string& insert(size_type pos1, const basic_string& str,
25+
// size_type pos2, size_type n=npos); // constexpr since C++20
26+
// template <class T>
27+
// basic_string& insert(size_type pos1, const T& t, size_type pos2, size_type n=npos); // C++17, constexpr since C++20
28+
// basic_string& erase(size_type pos = 0, size_type n = npos); // constexpr since C++20
29+
// basic_string& replace(size_type pos, size_type n1, const value_type* s, size_type n2); // constexpr since C++20
30+
// basic_string& replace(size_type pos, size_type n1, size_type n2, value_type c); // constexpr since C++20
31+
// basic_string& replace(size_type pos1, size_type n1, const basic_string& str,
32+
// size_type pos2, size_type n2=npos); // constexpr since C++20
33+
// template <class T>
34+
// basic_string& replace(size_type pos1, size_type n1, const T& t,
35+
// size_type pos2, size_type n2= npos); // C++17, constexpr since C++20
36+
// size_type copy(value_type* s, size_type n, size_type pos = 0) const; // constexpr since C++20
37+
// template <class T>
38+
// int compare(const T& t) const noexcept; // C++17, constexpr since C++20
39+
// int compare(size_type pos1, size_type n1, const value_type* s, size_type n2) const; // constexpr since C++20
40+
41+
#include <cassert>
42+
#include <cstdint>
43+
#include <string>
44+
#include <string_view>
45+
46+
#include "sized_allocator.h"
47+
#include "test_macros.h"
48+
49+
template <class SizeT, class DiffT, class CharT = char, class Traits = std::char_traits<CharT> >
50+
TEST_CONSTEXPR_CXX20 void test_with_custom_size_type() {
51+
using Alloc = sized_allocator<CharT, SizeT, DiffT>;
52+
using string = std::basic_string<CharT, Traits, Alloc>;
53+
string s = "hello world";
54+
55+
// The following tests validate all possible calls to std::min within <basic_string>
56+
{ // basic_string(const basic_string& str, size_type pos, size_type n, const Allocator& a = Allocator())
57+
assert(string(s, 0, 5) == "hello");
58+
assert(string(s, 0, 5, Alloc(3)) == "hello");
59+
assert(string(s, 6, 5) == "world");
60+
assert(string(s, 6, 5, Alloc(3)) == "world");
61+
assert(string(s, 6, 100) == "world");
62+
assert(string(s, 6, 100, Alloc(3)) == "world");
63+
}
64+
#if TEST_STD_VER >= 23
65+
{ // constexpr basic_string(basic_string&& str, size_type pos, size_type n, const Allocator& a = Allocator());
66+
assert(string(string(s), 0, 5) == "hello");
67+
assert(string(string(s), 0, 5, Alloc(3)) == "hello");
68+
assert(string(string(s), 6, 5) == "world");
69+
assert(string(string(s), 6, 5, Alloc(3)) == "world");
70+
assert(string(string(s), 6, 100) == "world");
71+
assert(string(string(s), 6, 100, Alloc(3)) == "world");
72+
}
73+
#endif
74+
{ // basic_string& assign(const basic_string& str, size_type pos, size_type n=npos)
75+
string s1 = s;
76+
string s2 = "cplusplus";
77+
s1.assign(s2, 0, 5);
78+
assert(s1 == "cplus");
79+
s1.assign(s2, 0, 9);
80+
assert(s1 == "cplusplus");
81+
s1.assign(s2, 5);
82+
assert(s1 == "plus");
83+
s1.assign(s2, 0, 100);
84+
assert(s1 == "cplusplus");
85+
s1.assign(s2, 4);
86+
assert(s1 == "splus");
87+
s1.assign(s2, 0);
88+
assert(s1 == "cplusplus");
89+
}
90+
#if TEST_STD_VER >= 17
91+
{ // template <class T> basic_string& assign(const T& t, size_type pos, size_type n=npos)
92+
std::string_view sv = "cplusplus";
93+
string s1 = s;
94+
s1.assign(sv, 0, 5);
95+
assert(s1 == "cplus");
96+
s1.assign(sv, 0, 9);
97+
assert(s1 == "cplusplus");
98+
s1.assign(sv, 5);
99+
assert(s1 == "plus");
100+
s1.assign(sv, 0, 100);
101+
assert(s1 == "cplusplus");
102+
s1.assign(sv, 4);
103+
assert(s1 == "splus");
104+
s1.assign(sv, 0);
105+
assert(s1 == "cplusplus");
106+
}
107+
#endif
108+
{ // basic_string& append(const basic_string& str, size_type pos, size_type n=npos)
109+
string s1 = s;
110+
string s2 = " of cplusplus";
111+
s1.append(s2, 0, 5);
112+
assert(s1 == "hello world of c");
113+
s1 = s;
114+
s1.append(s2, 0, 100);
115+
assert(s1 == "hello world of cplusplus");
116+
s1 = s;
117+
s1.append(s2, 0);
118+
assert(s1 == "hello world of cplusplus");
119+
}
120+
#if TEST_STD_VER >= 17
121+
{ // template <class T> basic_string& append(const T& t, size_type pos, size_type n=npos)
122+
string s1 = s;
123+
std::string_view sv = " of cplusplus";
124+
s1.append(sv, 0, 5);
125+
assert(s1 == "hello world of c");
126+
s1 = s;
127+
s1.append(sv, 0, 100);
128+
assert(s1 == "hello world of cplusplus");
129+
s1 = s;
130+
s1.append(sv, 0);
131+
assert(s1 == "hello world of cplusplus");
132+
}
133+
#endif
134+
{ // basic_string& insert(size_type pos1, const basic_string& str, size_type pos2, size_type n=npos)
135+
string s1 = s;
136+
string s2 = " cplusplus";
137+
s1.insert(5, s2, 0, 2);
138+
assert(s1 == "hello c world");
139+
s1 = s;
140+
s1.insert(5, s2, 0, 100);
141+
assert(s1 == "hello cplusplus world");
142+
s1 = s;
143+
s1.insert(5, s2, 0);
144+
assert(s1 == "hello cplusplus world");
145+
}
146+
#if TEST_STD_VER >= 17
147+
{ // template <class T> basic_string& insert(size_type pos1, const T& t, size_type pos2, size_type n=npos)
148+
string s1 = s;
149+
std::string_view sv = " cplusplus";
150+
s1.insert(5, sv, 0, 2);
151+
assert(s1 == "hello c world");
152+
s1 = s;
153+
s1.insert(5, sv, 0, 100);
154+
assert(s1 == "hello cplusplus world");
155+
s1 = s;
156+
s1.insert(5, sv, 0);
157+
assert(s1 == "hello cplusplus world");
158+
}
159+
#endif
160+
{ // basic_string& erase(size_type pos = 0, size_type n = npos)
161+
string s1 = s;
162+
assert(s1.erase(5, 100) == "hello");
163+
s1 = s;
164+
assert(s1.erase(5) == "hello");
165+
assert(s1.erase().empty());
166+
}
167+
{ // basic_string& replace(size_type pos, size_type n1, const value_type* s, size_type n2)
168+
string s1 = s;
169+
const char* s2 = "cpluscplus";
170+
assert(s1.replace(6, 5, s2, 1) == "hello c");
171+
assert(s1.replace(6, 1, s2, 10) == "hello cpluscplus");
172+
}
173+
{ // basic_string& replace(size_type pos, size_type n1, size_type n2, value_type c)
174+
string s1 = s;
175+
assert(s1.replace(5, 6, 2, 'o') == "hellooo");
176+
assert(s1.replace(5, 2, 0, 'o') == "hello");
177+
}
178+
{ // basic_string& replace(size_type pos1, size_type n1, const basic_string& str, size_type pos2, size_type n2=npos)
179+
string s1 = s;
180+
string s2 = "cplusplus";
181+
assert(s1.replace(6, 5, s2, 0, 1) == "hello c");
182+
assert(s1.replace(7, 0, s2, 1, 9) == "hello cplusplus");
183+
s1 = s;
184+
assert(s1.replace(6, 5, s2, 0, 100) == "hello cplusplus");
185+
s1 = s;
186+
assert(s1.replace(6, 5, s2, 0) == "hello cplusplus");
187+
}
188+
#if TEST_STD_VER >= 17
189+
{ // template <class T> basic_string& replace(size_type pos1, size_type n1, const T& t, size_type pos2, size_type n2= npos)
190+
string s1 = s;
191+
std::string_view sv = "cplusplus";
192+
assert(s1.replace(6, 5, sv, 0, 1) == "hello c");
193+
assert(s1.replace(7, 0, sv, 1, 9) == "hello cplusplus");
194+
s1 = s;
195+
assert(s1.replace(6, 5, sv, 0, 100) == "hello cplusplus");
196+
s1 = s;
197+
assert(s1.replace(6, 5, sv, 0) == "hello cplusplus");
198+
}
199+
#endif
200+
{ // size_type copy(value_type* s, size_type n, size_type pos = 0) const
201+
string s1 = s;
202+
char bar[100] = {};
203+
s1.copy(bar, s1.size());
204+
assert(s1 == bar);
205+
}
206+
{ // int compare(size_type pos1, size_type n1, const value_type* s, size_type n2) const
207+
string s1 = s;
208+
assert(s1.compare(0, 11, "hello world", 11) == 0);
209+
assert(s1.compare(0, 11, "hello", 11) > 0);
210+
assert(s1.compare(0, 11, "hello world C++", 100) < 0);
211+
}
212+
#if TEST_STD_VER >= 17
213+
{ // template <class T> int compare(const T& t) const noexcept;
214+
using std::operator""sv;
215+
string s1 = s;
216+
assert(s1.compare("hello world"sv) == 0);
217+
assert(s1.compare("hello"sv) > 0);
218+
assert(s1.compare("hello world C++"sv) < 0);
219+
}
220+
#endif
221+
}
222+
223+
TEST_CONSTEXPR_CXX20 bool test() {
224+
test_with_custom_size_type<unsigned char, signed char>();
225+
test_with_custom_size_type<unsigned short, short>();
226+
test_with_custom_size_type<unsigned short, short>();
227+
test_with_custom_size_type<unsigned, int>();
228+
test_with_custom_size_type<std::size_t, std::ptrdiff_t>();
229+
test_with_custom_size_type<std::uint8_t, std::int8_t>();
230+
test_with_custom_size_type<std::uint16_t, std::int16_t>();
231+
test_with_custom_size_type<std::uint32_t, std::int32_t>();
232+
test_with_custom_size_type<std::uint64_t, std::int64_t>();
233+
234+
return true;
235+
}
236+
237+
int main(int, char**) {
238+
test();
239+
#if TEST_STD_VER > 17
240+
static_assert(test());
241+
#endif
242+
243+
return 0;
244+
}

0 commit comments

Comments
 (0)