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+ // <vector>
10+ // vector<bool>
11+
12+ // This test examines ambiguous calls to std::max in vector<bool>
13+ // Fix https://github.com/llvm/llvm-project/issues/121713
14+
15+ #include < cassert>
16+ #include < cstddef>
17+ #include < cstdint>
18+ #include < limits>
19+ #include < memory>
20+ #include < new>
21+ #include < vector>
22+
23+ #include " test_macros.h"
24+
25+ template <typename T, typename SIZE_TYPE = std::size_t , typename DIFF_TYPE = std::ptrdiff_t >
26+ class sized_allocator {
27+ template <typename U, typename Sz, typename Diff>
28+ friend class sized_allocator ;
29+
30+ public:
31+ using value_type = T;
32+ using size_type = SIZE_TYPE;
33+ using difference_type = DIFF_TYPE;
34+ using propagate_on_container_swap = std::true_type;
35+
36+ TEST_CONSTEXPR_CXX20 explicit sized_allocator (int d = 0 ) : data_(d) {}
37+
38+ template <typename U, typename Sz, typename Diff>
39+ TEST_CONSTEXPR_CXX20 sized_allocator (const sized_allocator<U, Sz, Diff>& a) TEST_NOEXCEPT : data_(a.data_) {}
40+
41+ TEST_CONSTEXPR_CXX20 T* allocate (size_type n) {
42+ if (n > max_size ())
43+ TEST_THROW (std::bad_array_new_length ());
44+ return std::allocator<T>().allocate (n);
45+ }
46+
47+ TEST_CONSTEXPR_CXX20 void deallocate (T* p, size_type n) TEST_NOEXCEPT { std::allocator<T>().deallocate (p, n); }
48+
49+ TEST_CONSTEXPR size_type max_size () const TEST_NOEXCEPT {
50+ return std::numeric_limits<size_type>::max () / sizeof (value_type);
51+ }
52+
53+ private:
54+ int data_;
55+
56+ TEST_CONSTEXPR friend bool operator ==(const sized_allocator& a, const sized_allocator& b) {
57+ return a.data_ == b.data_ ;
58+ }
59+ TEST_CONSTEXPR friend bool operator !=(const sized_allocator& a, const sized_allocator& b) {
60+ return a.data_ != b.data_ ;
61+ }
62+ };
63+
64+ TEST_CONSTEXPR_CXX20 bool tests () {
65+ // The following tests are typical ways to trigger reallocations where `std::max` is used to calculate the capacity.
66+ {
67+ using Alloc = sized_allocator<bool , std::uint8_t , std::int8_t >;
68+ std::vector<bool , Alloc> c{Alloc{1 }};
69+ c.resize (10 );
70+ }
71+ {
72+ using Alloc = sized_allocator<bool , std::uint8_t , std::int8_t >;
73+ std::vector<bool , Alloc> c{Alloc{1 }};
74+ c.assign (10 , true );
75+ }
76+ {
77+ using Alloc = sized_allocator<bool , std::uint8_t , std::int8_t >;
78+ std::vector<bool , Alloc> c{Alloc{1 }};
79+ c.insert (c.end (), true );
80+ }
81+ {
82+ using Alloc = sized_allocator<bool , std::uint16_t , std::int16_t >;
83+ std::vector<bool , Alloc> c{Alloc{1 }};
84+ c.insert (c.end (), 10 , true );
85+ }
86+ {
87+ using Alloc = sized_allocator<bool , std::uint16_t , std::int16_t >;
88+ std::vector<bool , Alloc> c{Alloc{1 }};
89+ c.push_back (true );
90+ }
91+ {
92+ using Alloc = sized_allocator<bool , std::uint16_t , std::int16_t >;
93+ std::vector<bool , Alloc> c{Alloc{1 }};
94+ c.resize (10 , true );
95+ }
96+ {
97+ using Alloc = sized_allocator<bool , std::uint32_t , std::int32_t >;
98+ std::vector<bool , Alloc> c{Alloc{1 }};
99+ c.resize (10 );
100+ }
101+ {
102+ using Alloc = sized_allocator<bool , std::uint64_t , std::int64_t >;
103+ std::vector<bool , Alloc> c{Alloc{1 }};
104+ c.resize (10 );
105+ }
106+ {
107+ using Alloc = sized_allocator<bool , std::size_t , std::ptrdiff_t >;
108+ std::vector<bool , Alloc> c{Alloc{1 }};
109+ c.resize (10 );
110+ }
111+
112+ return true ;
113+ }
114+
115+ int main (int , char **) {
116+ tests ();
117+ #if TEST_STD_VER >= 20
118+ static_assert (tests ());
119+ #endif
120+ return 0 ;
121+ }
0 commit comments