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