1 //===----------------------------------------------------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is dual licensed under the MIT and the University of Illinois Open
6 // Source Licenses. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #ifndef USES_ALLOC_TYPES_HPP
11 #define USES_ALLOC_TYPES_HPP
12
13 # include <memory>
14 # include <cassert>
15 #include <cstdlib>
16
17 #include "test_macros.h"
18 #include "test_workarounds.h"
19 #include "type_id.h"
20
21 // There are two forms of uses-allocator construction:
22 // (1) UA_AllocArg: 'T(allocator_arg_t, Alloc const&, Args&&...)'
23 // (2) UA_AllocLast: 'T(Args&&..., Alloc const&)'
24 // 'UA_None' represents non-uses allocator construction.
25 enum class UsesAllocatorType {
26 UA_None = 0,
27 UA_AllocArg = 2,
28 UA_AllocLast = 4
29 };
30 constexpr UsesAllocatorType UA_None = UsesAllocatorType::UA_None;
31 constexpr UsesAllocatorType UA_AllocArg = UsesAllocatorType::UA_AllocArg;
32 constexpr UsesAllocatorType UA_AllocLast = UsesAllocatorType::UA_AllocLast;
33
toString(UsesAllocatorType UA)34 inline const char* toString(UsesAllocatorType UA) {
35 switch (UA) {
36 case UA_None:
37 return "UA_None";
38 case UA_AllocArg:
39 return "UA_AllocArg";
40 case UA_AllocLast:
41 return "UA_AllocLast";
42 default:
43 std::abort();
44 }
45 }
46
47 #define COMPARE_ALLOC_TYPE(LHS, RHS) CompareVerbose(#LHS, LHS, #RHS, RHS)
48
CompareVerbose(const char * LHSString,UsesAllocatorType LHS,const char * RHSString,UsesAllocatorType RHS)49 inline bool CompareVerbose(const char* LHSString, UsesAllocatorType LHS,
50 const char* RHSString, UsesAllocatorType RHS) {
51 if (LHS == RHS)
52 return true;
53 std::printf("UsesAllocatorType's don't match:\n%s %s\n----------\n%s %s\n",
54 LHSString, toString(LHS), RHSString, toString(RHS));
55 return false;
56 }
57
58 template <class Alloc, std::size_t N>
59 class UsesAllocatorV1;
60 // Implements form (1) of uses-allocator construction from the specified
61 // 'Alloc' type and exactly 'N' additional arguments. It also provides
62 // non-uses allocator construction from 'N' arguments. This test type
63 // blows up when form (2) of uses-allocator is even considered.
64
65 template <class Alloc, std::size_t N>
66 class UsesAllocatorV2;
67 // Implements form (2) of uses-allocator construction from the specified
68 // 'Alloc' type and exactly 'N' additional arguments. It also provides
69 // non-uses allocator construction from 'N' arguments.
70
71 template <class Alloc, std::size_t N>
72 class UsesAllocatorV3;
73 // Implements both form (1) and (2) of uses-allocator construction from
74 // the specified 'Alloc' type and exactly 'N' additional arguments. It also
75 // provides non-uses allocator construction from 'N' arguments.
76
77 template <class Alloc, std::size_t>
78 class NotUsesAllocator;
79 // Implements both form (1) and (2) of uses-allocator construction from
80 // the specified 'Alloc' type and exactly 'N' additional arguments. It also
81 // provides non-uses allocator construction from 'N' arguments. However
82 // 'NotUsesAllocator' never provides a 'allocator_type' typedef so it is
83 // never automatically uses-allocator constructed.
84
85
86 template <class ...ArgTypes, class TestType>
checkConstruct(TestType & value,UsesAllocatorType form,typename TestType::CtorAlloc const & alloc)87 bool checkConstruct(TestType& value, UsesAllocatorType form,
88 typename TestType::CtorAlloc const& alloc)
89 // Check that 'value' was constructed using the specified 'form' of
90 // construction and with the specified 'ArgTypes...'. Additionally
91 // check that 'value' was constructed using the specified 'alloc'.
92 {
93 if (form == UA_None) {
94 return value.template checkConstruct<ArgTypes&&...>(form);
95 } else {
96 return value.template checkConstruct<ArgTypes&&...>(form, alloc);
97 }
98 }
99
100
101 template <class ...ArgTypes, class TestType>
checkConstruct(TestType & value,UsesAllocatorType form)102 bool checkConstruct(TestType& value, UsesAllocatorType form) {
103 return value.template checkConstruct<ArgTypes&&...>(form);
104 }
105
106 template <class TestType>
checkConstructionEquiv(TestType & T,TestType & U)107 bool checkConstructionEquiv(TestType& T, TestType& U)
108 // check that 'T' and 'U' where initialized in the exact same manner.
109 {
110 return T.checkConstructEquiv(U);
111 }
112
113 ////////////////////////////////////////////////////////////////////////////////
114 namespace detail {
115
116 template <bool IsZero, size_t N, class ArgList, class ...Args>
117 struct TakeNImp;
118
119 template <class ArgList, class ...Args>
120 struct TakeNImp<true, 0, ArgList, Args...> {
121 typedef ArgList type;
122 };
123
124 template <size_t N, class ...A1, class F, class ...R>
125 struct TakeNImp<false, N, ArgumentListID<A1...>, F, R...>
126 : TakeNImp<N-1 == 0, N - 1, ArgumentListID<A1..., F>, R...> {};
127
128 template <size_t N, class ...Args>
129 struct TakeNArgs : TakeNImp<N == 0, N, ArgumentListID<>, Args...> {};
130
131 template <class T>
132 struct Identity { typedef T type; };
133
134 template <class T>
135 using IdentityT = typename Identity<T>::type;
136
137 template <bool Value>
138 using EnableIfB = typename std::enable_if<Value, bool>::type;
139
140 } // end namespace detail
141
142 // FIXME: UsesAllocatorTestBase needs some special logic to deal with
143 // polymorphic allocators. However we don't want to include
144 // <experimental/memory_resource> in this header. Therefore in order
145 // to inject this behavior later we use a trait.
146 // See test_memory_resource.hpp for more info.
147 template <class Alloc>
148 struct TransformErasedTypeAlloc {
149 using type = Alloc;
150 };
151
152 using detail::EnableIfB;
153
154 struct AllocLastTag {};
155
156 template <class Alloc, bool = std::is_default_constructible<Alloc>::value>
157 struct UsesAllocatorTestBaseStorage {
158 Alloc allocator;
159 UsesAllocatorTestBaseStorage() = default;
UsesAllocatorTestBaseStorageUsesAllocatorTestBaseStorage160 UsesAllocatorTestBaseStorage(Alloc const& a) : allocator(a) {}
get_allocatorUsesAllocatorTestBaseStorage161 const Alloc* get_allocator() const { return &allocator; }
162 };
163
164 template <class Alloc>
165 struct UsesAllocatorTestBaseStorage<Alloc, false> {
166 union {
167 char dummy;
168 Alloc alloc;
169 };
170 bool has_alloc = false;
171
UsesAllocatorTestBaseStorageUsesAllocatorTestBaseStorage172 UsesAllocatorTestBaseStorage() : dummy(), has_alloc(false) {}
UsesAllocatorTestBaseStorageUsesAllocatorTestBaseStorage173 UsesAllocatorTestBaseStorage(Alloc const& a) : alloc(a), has_alloc(true) {}
~UsesAllocatorTestBaseStorageUsesAllocatorTestBaseStorage174 ~UsesAllocatorTestBaseStorage() {
175 if (has_alloc)
176 alloc.~Alloc();
177 }
178
get_allocatorUsesAllocatorTestBaseStorage179 Alloc const* get_allocator() const {
180 if (!has_alloc)
181 return nullptr;
182 return &alloc;
183 }
184 };
185
186 template <class Self, class Alloc>
187 struct UsesAllocatorTestBase {
188 public:
189 using CtorAlloc = typename TransformErasedTypeAlloc<Alloc>::type;
190
191 template <class ...ArgTypes>
checkConstructUsesAllocatorTestBase192 bool checkConstruct(UsesAllocatorType expectType) const {
193 auto expectArgs = &makeArgumentID<ArgTypes...>();
194 return COMPARE_ALLOC_TYPE(expectType, constructor_called) &&
195 COMPARE_TYPEID(args_id, expectArgs);
196 }
197
198 template <class ...ArgTypes>
checkConstructUsesAllocatorTestBase199 bool checkConstruct(UsesAllocatorType expectType,
200 CtorAlloc const& expectAlloc) const {
201 auto ExpectID = &makeArgumentID<ArgTypes...>() ;
202 return COMPARE_ALLOC_TYPE(expectType, constructor_called) &&
203 COMPARE_TYPEID(args_id, ExpectID) &&
204 has_alloc() && expectAlloc == *get_alloc();
205
206 }
207
checkConstructEquivUsesAllocatorTestBase208 bool checkConstructEquiv(UsesAllocatorTestBase& O) const {
209 if (has_alloc() != O.has_alloc())
210 return false;
211 return COMPARE_ALLOC_TYPE(constructor_called, O.constructor_called)
212 && COMPARE_TYPEID(args_id, O.args_id)
213 && (!has_alloc() || *get_alloc() == *O.get_alloc());
214 }
215
216 protected:
UsesAllocatorTestBaseUsesAllocatorTestBase217 explicit UsesAllocatorTestBase(const TypeID* aid)
218 : args_id(aid), constructor_called(UA_None), alloc_store()
219 {}
220
UsesAllocatorTestBaseUsesAllocatorTestBase221 UsesAllocatorTestBase(UsesAllocatorTestBase const&)
222 : args_id(&makeArgumentID<Self const&>()), constructor_called(UA_None),
223 alloc_store()
224 {}
225
UsesAllocatorTestBaseUsesAllocatorTestBase226 UsesAllocatorTestBase(UsesAllocatorTestBase&&)
227 : args_id(&makeArgumentID<Self&&>()), constructor_called(UA_None),
228 alloc_store()
229 {}
230
231 template <class ...Args>
UsesAllocatorTestBaseUsesAllocatorTestBase232 UsesAllocatorTestBase(std::allocator_arg_t, CtorAlloc const& a, Args&&...)
233 : args_id(&makeArgumentID<Args&&...>()),
234 constructor_called(UA_AllocArg),
235 alloc_store(a)
236 {}
237
238 template <class ...Args, class ArgsIDL = detail::TakeNArgs<sizeof...(Args) - 1, Args&&...>>
UsesAllocatorTestBaseUsesAllocatorTestBase239 UsesAllocatorTestBase(AllocLastTag, Args&&... args)
240 : args_id(&makeTypeIDImp<typename ArgsIDL::type>()),
241 constructor_called(UA_AllocLast),
242 alloc_store(UsesAllocatorTestBase::getAllocatorFromPack(
243 typename ArgsIDL::type{},
244 std::forward<Args>(args)...))
245 {
246 }
247
248 private:
249 template <class ...LArgs, class ...Args>
getAllocatorFromPackUsesAllocatorTestBase250 static CtorAlloc getAllocatorFromPack(ArgumentListID<LArgs...>, Args&&... args) {
251 return UsesAllocatorTestBase::getAllocatorFromPackImp<LArgs const&...>(args...);
252 }
253
254 template <class ...LArgs>
getAllocatorFromPackImpUsesAllocatorTestBase255 static CtorAlloc getAllocatorFromPackImp(
256 typename detail::Identity<LArgs>::type..., CtorAlloc const& alloc) {
257 return alloc;
258 }
259
has_allocUsesAllocatorTestBase260 bool has_alloc() const { return alloc_store.get_allocator() != nullptr; }
get_allocUsesAllocatorTestBase261 const CtorAlloc *get_alloc() const { return alloc_store.get_allocator(); }
262 public:
263 const TypeID* args_id;
264 UsesAllocatorType constructor_called = UA_None;
265 UsesAllocatorTestBaseStorage<CtorAlloc> alloc_store;
266 };
267
268 template <class Alloc, size_t Arity>
269 class UsesAllocatorV1 : public UsesAllocatorTestBase<UsesAllocatorV1<Alloc, Arity>, Alloc>
270 {
271 public:
272 typedef Alloc allocator_type;
273
274 using Base = UsesAllocatorTestBase<UsesAllocatorV1, Alloc>;
275 using CtorAlloc = typename Base::CtorAlloc;
276
UsesAllocatorV1()277 UsesAllocatorV1() : Base(&makeArgumentID<>()) {}
278
UsesAllocatorV1(UsesAllocatorV1 const &)279 UsesAllocatorV1(UsesAllocatorV1 const&)
280 : Base(&makeArgumentID<UsesAllocatorV1 const&>()) {}
UsesAllocatorV1(UsesAllocatorV1 &&)281 UsesAllocatorV1(UsesAllocatorV1 &&)
282 : Base(&makeArgumentID<UsesAllocatorV1 &&>()) {}
283 // Non-Uses Allocator Ctor
284 template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
UsesAllocatorV1(Args &&...)285 UsesAllocatorV1(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
286
287 // Uses Allocator Arg Ctor
288 template <class ...Args>
UsesAllocatorV1(std::allocator_arg_t tag,CtorAlloc const & a,Args &&...args)289 UsesAllocatorV1(std::allocator_arg_t tag, CtorAlloc const & a, Args&&... args)
290 : Base(tag, a, std::forward<Args>(args)...)
291 { }
292
293 // BLOWS UP: Uses Allocator Last Ctor
294 template <class First, class ...Args, EnableIfB<sizeof...(Args) == Arity> Dummy = false>
UsesAllocatorV1(First &&,Args &&...)295 constexpr UsesAllocatorV1(First&&, Args&&...)
296 {
297 static_assert(!std::is_same<First, First>::value, "");
298 }
299 };
300
301
302 template <class Alloc, size_t Arity>
303 class UsesAllocatorV2 : public UsesAllocatorTestBase<UsesAllocatorV2<Alloc, Arity>, Alloc>
304 {
305 public:
306 typedef Alloc allocator_type;
307
308 using Base = UsesAllocatorTestBase<UsesAllocatorV2, Alloc>;
309 using CtorAlloc = typename Base::CtorAlloc;
310
UsesAllocatorV2()311 UsesAllocatorV2() : Base(&makeArgumentID<>()) {}
UsesAllocatorV2(UsesAllocatorV2 const &)312 UsesAllocatorV2(UsesAllocatorV2 const&)
313 : Base(&makeArgumentID<UsesAllocatorV2 const&>()) {}
UsesAllocatorV2(UsesAllocatorV2 &&)314 UsesAllocatorV2(UsesAllocatorV2 &&)
315 : Base(&makeArgumentID<UsesAllocatorV2 &&>()) {}
316
317 // Non-Uses Allocator Ctor
318 template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
UsesAllocatorV2(Args &&...)319 UsesAllocatorV2(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
320
321 // Uses Allocator Last Ctor
322 template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
UsesAllocatorV2(Args &&...args)323 UsesAllocatorV2(Args&&... args)
324 : Base(AllocLastTag{}, std::forward<Args>(args)...)
325 {}
326 };
327
328 template <class Alloc, size_t Arity>
329 class UsesAllocatorV3 : public UsesAllocatorTestBase<UsesAllocatorV3<Alloc, Arity>, Alloc>
330 {
331 public:
332 typedef Alloc allocator_type;
333
334 using Base = UsesAllocatorTestBase<UsesAllocatorV3, Alloc>;
335 using CtorAlloc = typename Base::CtorAlloc;
336
UsesAllocatorV3()337 UsesAllocatorV3() : Base(&makeArgumentID<>()) {}
UsesAllocatorV3(UsesAllocatorV3 const &)338 UsesAllocatorV3(UsesAllocatorV3 const&)
339 : Base(&makeArgumentID<UsesAllocatorV3 const&>()) {}
UsesAllocatorV3(UsesAllocatorV3 &&)340 UsesAllocatorV3(UsesAllocatorV3 &&)
341 : Base(&makeArgumentID<UsesAllocatorV3 &&>()) {}
342
343 // Non-Uses Allocator Ctor
344 template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
UsesAllocatorV3(Args &&...)345 UsesAllocatorV3(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
346
347 // Uses Allocator Arg Ctor
348 template <class ...Args>
UsesAllocatorV3(std::allocator_arg_t tag,CtorAlloc const & alloc,Args &&...args)349 UsesAllocatorV3(std::allocator_arg_t tag, CtorAlloc const& alloc, Args&&... args)
350 : Base(tag, alloc, std::forward<Args>(args)...)
351 {}
352
353 // Uses Allocator Last Ctor
354 template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
UsesAllocatorV3(Args &&...args)355 UsesAllocatorV3(Args&&... args)
356 : Base(AllocLastTag{}, std::forward<Args>(args)...)
357 {}
358 };
359
360 template <class Alloc, size_t Arity>
361 class NotUsesAllocator : public UsesAllocatorTestBase<NotUsesAllocator<Alloc, Arity>, Alloc>
362 {
363 public:
364 // no allocator_type typedef provided
365
366 using Base = UsesAllocatorTestBase<NotUsesAllocator, Alloc>;
367 using CtorAlloc = typename Base::CtorAlloc;
368
NotUsesAllocator()369 NotUsesAllocator() : Base(&makeArgumentID<>()) {}
NotUsesAllocator(NotUsesAllocator const &)370 NotUsesAllocator(NotUsesAllocator const&)
371 : Base(&makeArgumentID<NotUsesAllocator const&>()) {}
NotUsesAllocator(NotUsesAllocator &&)372 NotUsesAllocator(NotUsesAllocator &&)
373 : Base(&makeArgumentID<NotUsesAllocator &&>()) {}
374 // Non-Uses Allocator Ctor
375 template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
NotUsesAllocator(Args &&...)376 NotUsesAllocator(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
377
378 // Uses Allocator Arg Ctor
379 template <class ...Args>
NotUsesAllocator(std::allocator_arg_t tag,CtorAlloc const & alloc,Args &&...args)380 NotUsesAllocator(std::allocator_arg_t tag, CtorAlloc const& alloc, Args&&... args)
381 : Base(tag, alloc, std::forward<Args>(args)...)
382 {}
383
384 // Uses Allocator Last Ctor
385 template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
NotUsesAllocator(Args &&...args)386 NotUsesAllocator(Args&&... args)
387 : Base(AllocLastTag{}, std::forward<Args>(args)...)
388 {}
389 };
390
391 #endif /* USES_ALLOC_TYPES_HPP */
392