xref: /llvm-project/libcxx/test/support/uses_alloc_types.h (revision 953af0e7f1bcb42136be1a0ea9cdd5aa1fb74852)
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 #ifndef USES_ALLOC_TYPES_H
10 #define USES_ALLOC_TYPES_H
11 
12 #include <cassert>
13 #include <cstdlib>
14 #include <memory>
15 
16 #include "test_macros.h"
17 #include "test_workarounds.h"
18 #include "type_id.h"
19 
20 // There are two forms of uses-allocator construction:
21 //   (1) UA_AllocArg: 'T(allocator_arg_t, Alloc const&, Args&&...)'
22 //   (2) UA_AllocLast: 'T(Args&&..., Alloc const&)'
23 // 'UA_None' represents non-uses allocator construction.
24 enum class UsesAllocatorType { UA_None = 0, UA_AllocArg = 2, UA_AllocLast = 4 };
25 constexpr UsesAllocatorType UA_None      = UsesAllocatorType::UA_None;
26 constexpr UsesAllocatorType UA_AllocArg  = UsesAllocatorType::UA_AllocArg;
27 constexpr UsesAllocatorType UA_AllocLast = UsesAllocatorType::UA_AllocLast;
28 
29 inline const char* toString(UsesAllocatorType UA) {
30   switch (UA) {
31   case UA_None:
32     return "UA_None";
33   case UA_AllocArg:
34     return "UA_AllocArg";
35   case UA_AllocLast:
36     return "UA_AllocLast";
37   default:
38     std::abort();
39   }
40 }
41 
42 template <class Alloc, std::size_t N>
43 class UsesAllocatorV1;
44 // Implements form (1) of uses-allocator construction from the specified
45 // 'Alloc' type and exactly 'N' additional arguments. It also provides
46 // non-uses allocator construction from 'N' arguments. This test type
47 // blows up when form (2) of uses-allocator is even considered.
48 
49 template <class Alloc, std::size_t N>
50 class UsesAllocatorV2;
51 // Implements form (2) of uses-allocator construction from the specified
52 // 'Alloc' type and exactly 'N' additional arguments. It also provides
53 // non-uses allocator construction from 'N' arguments.
54 
55 template <class Alloc, std::size_t N>
56 class UsesAllocatorV3;
57 // Implements both form (1) and (2) of uses-allocator construction from
58 // the specified 'Alloc' type and exactly 'N' additional arguments. It also
59 // provides non-uses allocator construction from 'N' arguments.
60 
61 template <class Alloc, std::size_t>
62 class NotUsesAllocator;
63 // Implements both form (1) and (2) of uses-allocator construction from
64 // the specified 'Alloc' type and exactly 'N' additional arguments. It also
65 // provides non-uses allocator construction from 'N' arguments. However
66 // 'NotUsesAllocator' never provides a 'allocator_type' typedef so it is
67 // never automatically uses-allocator constructed.
68 
69 template <class... ArgTypes, class TestType>
70 bool checkConstruct(TestType& value, UsesAllocatorType form, typename TestType::CtorAlloc const& alloc)
71 // Check that 'value' was constructed using the specified 'form' of
72 // construction and with the specified 'ArgTypes...'. Additionally
73 // check that 'value' was constructed using the specified 'alloc'.
74 {
75   if (form == UA_None) {
76     return value.template checkConstruct<ArgTypes&&...>(form);
77   } else {
78     return value.template checkConstruct<ArgTypes&&...>(form, alloc);
79   }
80 }
81 
82 template <class... ArgTypes, class TestType>
83 bool checkConstruct(TestType& value, UsesAllocatorType form) {
84   return value.template checkConstruct<ArgTypes&&...>(form);
85 }
86 
87 template <class TestType>
88 bool checkConstructionEquiv(TestType& T, TestType& U)
89 // check that 'T' and 'U' where initialized in the exact same manner.
90 {
91   return T.checkConstructEquiv(U);
92 }
93 
94 ////////////////////////////////////////////////////////////////////////////////
95 namespace detail {
96 
97 template <bool IsZero, std::size_t N, class ArgList, class... Args>
98 struct TakeNImp;
99 
100 template <class ArgList, class... Args>
101 struct TakeNImp<true, 0, ArgList, Args...> {
102   typedef ArgList type;
103 };
104 
105 template <std::size_t N, class... A1, class F, class... R>
106 struct TakeNImp<false, N, ArgumentListID<A1...>, F, R...>
107     : TakeNImp<N - 1 == 0, N - 1, ArgumentListID<A1..., F>, R...> {};
108 
109 template <std::size_t N, class... Args>
110 struct TakeNArgs : TakeNImp<N == 0, N, ArgumentListID<>, Args...> {};
111 
112 template <class T>
113 struct Identity {
114   typedef T type;
115 };
116 
117 template <class T>
118 using IdentityT = typename Identity<T>::type;
119 
120 template <bool Value>
121 using EnableIfB = typename std::enable_if<Value, bool>::type;
122 
123 } // namespace detail
124 
125 using detail::EnableIfB;
126 
127 struct AllocLastTag {};
128 
129 template <class Alloc, bool = std::is_default_constructible<Alloc>::value>
130 struct UsesAllocatorTestBaseStorage {
131   Alloc allocator;
132   UsesAllocatorTestBaseStorage() = default;
133   UsesAllocatorTestBaseStorage(Alloc const& a) : allocator(a) {}
134   const Alloc* get_allocator() const { return &allocator; }
135 };
136 
137 template <class Alloc>
138 struct UsesAllocatorTestBaseStorage<Alloc, false> {
139   union {
140     char dummy;
141     Alloc alloc;
142   };
143   bool has_alloc = false;
144 
145   UsesAllocatorTestBaseStorage() : dummy(), has_alloc(false) {}
146   UsesAllocatorTestBaseStorage(Alloc const& a) : alloc(a), has_alloc(true) {}
147   ~UsesAllocatorTestBaseStorage() {
148     if (has_alloc)
149       alloc.~Alloc();
150   }
151 
152   Alloc const* get_allocator() const {
153     if (!has_alloc)
154       return nullptr;
155     return &alloc;
156   }
157 };
158 
159 template <class Self, class Alloc>
160 struct UsesAllocatorTestBase {
161 public:
162   using CtorAlloc = Alloc;
163 
164   template <class... ArgTypes>
165   bool checkConstruct(UsesAllocatorType expectType) const {
166     auto expectArgs = &makeArgumentID<ArgTypes...>();
167     if (expectType != constructor_called)
168       return false;
169     if (args_id != expectArgs)
170       return false;
171     return true;
172   }
173 
174   template <class... ArgTypes>
175   bool checkConstruct(UsesAllocatorType expectType, CtorAlloc const& expectAlloc) const {
176     auto ExpectID = &makeArgumentID<ArgTypes...>();
177     if (expectType != constructor_called)
178       return false;
179     if (args_id != ExpectID)
180       return false;
181     if (!has_alloc() || expectAlloc != *get_alloc())
182       return false;
183     return true;
184   }
185 
186   bool checkConstructEquiv(UsesAllocatorTestBase& O) const {
187     if (has_alloc() != O.has_alloc())
188       return false;
189     if (constructor_called != O.constructor_called)
190       return false;
191     if (args_id != O.args_id)
192       return false;
193     if (has_alloc() && *get_alloc() != *O.get_alloc())
194       return false;
195     return true;
196   }
197 
198 protected:
199   explicit UsesAllocatorTestBase(const TypeID* aid) : args_id(aid), constructor_called(UA_None), alloc_store() {}
200 
201   UsesAllocatorTestBase(UsesAllocatorTestBase const&)
202       : args_id(&makeArgumentID<Self const&>()), constructor_called(UA_None), alloc_store() {}
203 
204   UsesAllocatorTestBase(UsesAllocatorTestBase&&)
205       : args_id(&makeArgumentID<Self&&>()), constructor_called(UA_None), alloc_store() {}
206 
207   template <class... Args>
208   UsesAllocatorTestBase(std::allocator_arg_t, CtorAlloc const& a, Args&&...)
209       : args_id(&makeArgumentID<Args&&...>()), constructor_called(UA_AllocArg), alloc_store(a) {}
210 
211   template <class... Args, class ArgsIDL = detail::TakeNArgs<sizeof...(Args) - 1, Args&&...>>
212   UsesAllocatorTestBase(AllocLastTag, Args&&... args)
213       : args_id(&makeTypeIDImp<typename ArgsIDL::type>()),
214         constructor_called(UA_AllocLast),
215         alloc_store(
216             UsesAllocatorTestBase::getAllocatorFromPack(typename ArgsIDL::type{}, std::forward<Args>(args)...)) {}
217 
218 private:
219   template <class... LArgs, class... Args>
220   static CtorAlloc getAllocatorFromPack(ArgumentListID<LArgs...>, Args&&... args) {
221     return UsesAllocatorTestBase::getAllocatorFromPackImp<LArgs const&...>(args...);
222   }
223 
224   template <class... LArgs>
225   static CtorAlloc getAllocatorFromPackImp(typename detail::Identity<LArgs>::type..., CtorAlloc const& alloc) {
226     return alloc;
227   }
228 
229   bool has_alloc() const { return alloc_store.get_allocator() != nullptr; }
230   const CtorAlloc* get_alloc() const { return alloc_store.get_allocator(); }
231 
232 public:
233   const TypeID* args_id;
234   UsesAllocatorType constructor_called = UA_None;
235   UsesAllocatorTestBaseStorage<CtorAlloc> alloc_store;
236 };
237 
238 template <class Alloc, std::size_t Arity>
239 class UsesAllocatorV1 : public UsesAllocatorTestBase<UsesAllocatorV1<Alloc, Arity>, Alloc> {
240 public:
241   typedef Alloc allocator_type;
242 
243   using Base      = UsesAllocatorTestBase<UsesAllocatorV1, Alloc>;
244   using CtorAlloc = typename Base::CtorAlloc;
245 
246   UsesAllocatorV1() : Base(&makeArgumentID<>()) {}
247 
248   UsesAllocatorV1(UsesAllocatorV1 const&) : Base(&makeArgumentID<UsesAllocatorV1 const&>()) {}
249   UsesAllocatorV1(UsesAllocatorV1&&) : Base(&makeArgumentID<UsesAllocatorV1&&>()) {}
250   // Non-Uses Allocator Ctor
251   template <class... Args, EnableIfB<sizeof...(Args) == Arity> = false>
252   UsesAllocatorV1(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
253 
254   // Uses Allocator Arg Ctor
255   template <class... Args>
256   UsesAllocatorV1(std::allocator_arg_t tag, CtorAlloc const& a, Args&&... args)
257       : Base(tag, a, std::forward<Args>(args)...) {}
258 
259   // BLOWS UP: Uses Allocator Last Ctor
260   template <class First, class... Args, EnableIfB<sizeof...(Args) == Arity> Dummy = false>
261   constexpr UsesAllocatorV1(First&&, Args&&...) {
262     static_assert(!std::is_same<First, First>::value, "");
263   }
264 };
265 
266 template <class Alloc, std::size_t Arity>
267 class UsesAllocatorV2 : public UsesAllocatorTestBase<UsesAllocatorV2<Alloc, Arity>, Alloc> {
268 public:
269   typedef Alloc allocator_type;
270 
271   using Base      = UsesAllocatorTestBase<UsesAllocatorV2, Alloc>;
272   using CtorAlloc = typename Base::CtorAlloc;
273 
274   UsesAllocatorV2() : Base(&makeArgumentID<>()) {}
275   UsesAllocatorV2(UsesAllocatorV2 const&) : Base(&makeArgumentID<UsesAllocatorV2 const&>()) {}
276   UsesAllocatorV2(UsesAllocatorV2&&) : Base(&makeArgumentID<UsesAllocatorV2&&>()) {}
277 
278   // Non-Uses Allocator Ctor
279   template <class... Args, EnableIfB<sizeof...(Args) == Arity> = false>
280   UsesAllocatorV2(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
281 
282   // Uses Allocator Last Ctor
283   template <class... Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
284   UsesAllocatorV2(Args&&... args) : Base(AllocLastTag{}, std::forward<Args>(args)...) {}
285 };
286 
287 template <class Alloc, std::size_t Arity>
288 class UsesAllocatorV3 : public UsesAllocatorTestBase<UsesAllocatorV3<Alloc, Arity>, Alloc> {
289 public:
290   typedef Alloc allocator_type;
291 
292   using Base      = UsesAllocatorTestBase<UsesAllocatorV3, Alloc>;
293   using CtorAlloc = typename Base::CtorAlloc;
294 
295   UsesAllocatorV3() : Base(&makeArgumentID<>()) {}
296   UsesAllocatorV3(UsesAllocatorV3 const&) : Base(&makeArgumentID<UsesAllocatorV3 const&>()) {}
297   UsesAllocatorV3(UsesAllocatorV3&&) : Base(&makeArgumentID<UsesAllocatorV3&&>()) {}
298 
299   // Non-Uses Allocator Ctor
300   template <class... Args, EnableIfB<sizeof...(Args) == Arity> = false>
301   UsesAllocatorV3(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
302 
303   // Uses Allocator Arg Ctor
304   template <class... Args>
305   UsesAllocatorV3(std::allocator_arg_t tag, CtorAlloc const& alloc, Args&&... args)
306       : Base(tag, alloc, std::forward<Args>(args)...) {}
307 
308   // Uses Allocator Last Ctor
309   template <class... Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
310   UsesAllocatorV3(Args&&... args) : Base(AllocLastTag{}, std::forward<Args>(args)...) {}
311 };
312 
313 template <class Alloc, std::size_t Arity>
314 class NotUsesAllocator : public UsesAllocatorTestBase<NotUsesAllocator<Alloc, Arity>, Alloc> {
315 public:
316   // no allocator_type typedef provided
317 
318   using Base      = UsesAllocatorTestBase<NotUsesAllocator, Alloc>;
319   using CtorAlloc = typename Base::CtorAlloc;
320 
321   NotUsesAllocator() : Base(&makeArgumentID<>()) {}
322   NotUsesAllocator(NotUsesAllocator const&) : Base(&makeArgumentID<NotUsesAllocator const&>()) {}
323   NotUsesAllocator(NotUsesAllocator&&) : Base(&makeArgumentID<NotUsesAllocator&&>()) {}
324   // Non-Uses Allocator Ctor
325   template <class... Args, EnableIfB<sizeof...(Args) == Arity> = false>
326   NotUsesAllocator(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
327 
328   // Uses Allocator Arg Ctor
329   template <class... Args>
330   NotUsesAllocator(std::allocator_arg_t tag, CtorAlloc const& alloc, Args&&... args)
331       : Base(tag, alloc, std::forward<Args>(args)...) {}
332 
333   // Uses Allocator Last Ctor
334   template <class... Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
335   NotUsesAllocator(Args&&... args) : Base(AllocLastTag{}, std::forward<Args>(args)...) {}
336 };
337 
338 #endif /* USES_ALLOC_TYPES_H */
339