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 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20, c++23
10 
11 // <string>
12 
13 // [string.op.plus]
14 //
15 // template<class charT, class traits, class Allocator>
16 //   constexpr basic_string<charT, traits, Allocator>
17 //     operator+(const basic_string<charT, traits, Allocator>& lhs,
18 //               type_identity_t<basic_string_view<charT, traits>> rhs);                           // Since C++26
19 // template<class charT, class traits, class Allocator>
20 //   constexpr basic_string<charT, traits, Allocator>
21 //     operator+(basic_string<charT, traits, Allocator>&& lhs,
22 //               type_identity_t<basic_string_view<charT, traits>> rhs);                           // Since C++26
23 // template<class charT, class traits, class Allocator>
24 //   constexpr basic_string<charT, traits, Allocator>
25 //     operator+(type_identity_t<basic_string_view<charT, traits>> lhs,
26 //               const basic_string<charT, traits, Allocator>& rhs);                               // Since C++26
27 // template<class charT, class traits, class Allocator>
28 //   constexpr basic_string<charT, traits, Allocator>
29 //     operator+(type_identity_t<basic_string_view<charT, traits>> lhs,
30 //               basic_string<charT, traits, Allocator>&& rhs);                                    // Since C++26
31 
32 #include <cassert>
33 #include <concepts>
34 #include <string>
35 #include <utility>
36 
37 #include "asan_testing.h"
38 #include "constexpr_char_traits.h"
39 #include "make_string.h"
40 #include "min_allocator.h"
41 #include "test_allocator.h"
42 #include "test_macros.h"
43 
44 template <typename CharT, class TraitsT = std::char_traits<CharT>>
45 class ConvertibleToStringView {
46 public:
47   constexpr explicit ConvertibleToStringView(const CharT* cs) : cs_{cs} {}
48 
49   constexpr operator std::basic_string_view<CharT, TraitsT>() { return std::basic_string_view<CharT, TraitsT>(cs_); }
50   constexpr operator std::basic_string_view<CharT, TraitsT>() const {
51     return std::basic_string_view<CharT, TraitsT>(cs_);
52   }
53 
54 private:
55   const CharT* cs_;
56 };
57 
58 static_assert(std::constructible_from<std::basic_string_view<char>, const ConvertibleToStringView<char>>);
59 static_assert(std::convertible_to<const ConvertibleToStringView<char>, std::basic_string_view<char>>);
60 
61 static_assert(std::constructible_from<std::basic_string_view<char>, ConvertibleToStringView<char>>);
62 static_assert(std::convertible_to<ConvertibleToStringView<char>, std::basic_string_view<char>>);
63 
64 #define CS(S) MAKE_CSTRING(CharT, S)
65 
66 template <template <typename, typename> typename StringViewT, typename CharT, typename TraitsT, typename AllocT>
67 constexpr void test(const CharT* x, const CharT* y, const CharT* expected) {
68   AllocT allocator;
69 
70   // string& + string_view
71   {
72     std::basic_string<CharT, TraitsT, AllocT> st{x, allocator};
73     StringViewT<CharT, TraitsT> sv{y};
74 
75     std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = st + sv;
76     assert(result == expected);
77     assert(result.get_allocator() == allocator);
78     LIBCPP_ASSERT(is_string_asan_correct(st + sv));
79   }
80   // const string& + string_view
81   {
82     const std::basic_string<CharT, TraitsT, AllocT> st{x, allocator};
83     StringViewT<CharT, TraitsT> sv{y};
84 
85     std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = st + sv;
86     assert(result == expected);
87     assert(result.get_allocator() == allocator);
88     LIBCPP_ASSERT(is_string_asan_correct(st + sv));
89   }
90   // string&& + string_view
91   {
92     std::basic_string<CharT, TraitsT, AllocT> st{x, allocator};
93     StringViewT<CharT, TraitsT> sv{y};
94 
95     std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = std::move(st) + sv;
96     assert(result == expected);
97     assert(result.get_allocator() == allocator);
98     LIBCPP_ASSERT(is_string_asan_correct(std::move(st) + sv));
99   }
100   // string_view + string&
101   {
102     StringViewT<CharT, TraitsT> sv{x};
103     std::basic_string<CharT, TraitsT, AllocT> st{y, allocator};
104 
105     std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = sv + st;
106     assert(result == expected);
107     assert(result.get_allocator() == allocator);
108     LIBCPP_ASSERT(is_string_asan_correct(sv + st));
109   }
110   // string_view + const string&
111   {
112     StringViewT<CharT, TraitsT> sv{x};
113     const std::basic_string<CharT, TraitsT, AllocT> st{y, allocator};
114 
115     std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = sv + st;
116     assert(result == expected);
117     assert(result.get_allocator() == allocator);
118     LIBCPP_ASSERT(is_string_asan_correct(sv + st));
119   }
120   // string_view + string&&
121   {
122     // TODO: Remove workaround once https://github.com/llvm/llvm-project/issues/92382 is fixed.
123     // Create a `basic_string` to workaround clang bug:
124     // https://github.com/llvm/llvm-project/issues/92382
125     // Comparison between pointers to a string literal and some other object results in constant evaluation failure.
126     if constexpr (std::same_as<StringViewT<CharT, TraitsT>, std::basic_string_view<CharT, TraitsT>>) {
127       std::basic_string<CharT, TraitsT, AllocT> st_{x, allocator};
128       StringViewT<CharT, TraitsT> sv{st_};
129       std::basic_string<CharT, TraitsT, AllocT> st{y, allocator};
130 
131       std::same_as<std::basic_string<CharT, TraitsT, AllocT>> decltype(auto) result = sv + std::move(st);
132       assert(result == expected);
133       assert(result.get_allocator() == allocator);
134       LIBCPP_ASSERT(is_string_asan_correct(sv + std::move(st)));
135     }
136   }
137 }
138 
139 template <template <typename, typename> typename StringViewT,
140           typename CharT,
141           typename TraitsT,
142           typename AllocT = std::allocator<CharT>>
143 constexpr void test() {
144   // Concatenate with an empty `string`/`string_view`
145   test<StringViewT, CharT, TraitsT, AllocT>(CS(""), CS(""), CS(""));
146   test<StringViewT, CharT, TraitsT, AllocT>(CS(""), CS("short"), CS("short"));
147   test<StringViewT, CharT, TraitsT, AllocT>(CS(""), CS("not so short"), CS("not so short"));
148   test<StringViewT, CharT, TraitsT, AllocT>(
149       CS(""), CS("this is a much longer string"), CS("this is a much longer string"));
150 
151   test<StringViewT, CharT, TraitsT, AllocT>(CS(""), CS(""), CS(""));
152   test<StringViewT, CharT, TraitsT, AllocT>(CS("short"), CS(""), CS("short"));
153   test<StringViewT, CharT, TraitsT, AllocT>(CS("not so short"), CS(""), CS("not so short"));
154   test<StringViewT, CharT, TraitsT, AllocT>(
155       CS("this is a much longer string"), CS(""), CS("this is a much longer string"));
156 
157   // Non empty
158   test<StringViewT, CharT, TraitsT, AllocT>(CS("B"), CS("D"), CS("BD"));
159   test<StringViewT, CharT, TraitsT, AllocT>(CS("zmt94"), CS("+hkt82"), CS("zmt94+hkt82"));
160   test<StringViewT, CharT, TraitsT, AllocT>(CS("not so short"), CS("+is not bad"), CS("not so short+is not bad"));
161   test<StringViewT, CharT, TraitsT, AllocT>(
162       CS("this is a much longer string"),
163       CS("+which is so much better"),
164       CS("this is a much longer string+which is so much better"));
165 }
166 
167 template <template <typename, typename> typename StringViewT, typename CharT>
168 constexpr bool test() {
169   test<StringViewT, CharT, std::char_traits<CharT>>();
170   test<StringViewT, CharT, std::char_traits<CharT>, min_allocator<CharT>>();
171   test<StringViewT, CharT, std::char_traits<CharT>, safe_allocator<CharT>>();
172   test<StringViewT, CharT, std::char_traits<CharT>, test_allocator<CharT>>();
173 
174   test<StringViewT, CharT, constexpr_char_traits<CharT>>();
175   test<StringViewT, CharT, constexpr_char_traits<CharT>, min_allocator<CharT>>();
176   test<StringViewT, CharT, constexpr_char_traits<CharT>, safe_allocator<CharT>>();
177   test<StringViewT, CharT, constexpr_char_traits<CharT>, test_allocator<CharT>>();
178 
179   return true;
180 }
181 
182 int main(int, char**) {
183   // std::basic_string_view
184   test<std::basic_string_view, char>();
185   static_assert(test<std::basic_string_view, char>());
186 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
187   test<std::basic_string_view, wchar_t>();
188   static_assert(test<std::basic_string_view, wchar_t>());
189 #endif
190 #ifndef TEST_HAS_NO_CHAR8_T
191   test<std::basic_string_view, char8_t>();
192   static_assert(test<std::basic_string_view, char8_t>());
193 #endif
194   test<std::basic_string_view, char16_t>();
195   static_assert(test<std::basic_string_view, char16_t>());
196   test<std::basic_string_view, char32_t>();
197   static_assert(test<std::basic_string_view, char32_t>());
198 
199   // ConvertibleToStringView
200   test<ConvertibleToStringView, char>();
201   static_assert(test<ConvertibleToStringView, char>());
202 #ifndef TEST_HAS_NO_WIDE_CHARACTERS
203   test<ConvertibleToStringView, wchar_t>();
204   static_assert(test<ConvertibleToStringView, wchar_t>());
205 #endif
206 #ifndef TEST_HAS_NO_CHAR8_T
207   test<ConvertibleToStringView, char8_t>();
208   static_assert(test<ConvertibleToStringView, char8_t>());
209 #endif
210   test<ConvertibleToStringView, char16_t>();
211   static_assert(test<ConvertibleToStringView, char16_t>());
212   test<ConvertibleToStringView, char32_t>();
213   static_assert(test<ConvertibleToStringView, char32_t>());
214 
215   return 0;
216 }
217