xref: /aosp_15_r20/external/libcxx/test/support/uses_alloc_types.hpp (revision 58b9f456b02922dfdb1fad8a988d5fd8765ecb80)
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