Home | History | Annotate | Download | only in support
      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 
     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 
     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>
     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>
    102 bool checkConstruct(TestType& value, UsesAllocatorType form) {
    103     return value.template checkConstruct<ArgTypes&&...>(form);
    104 }
    105 
    106 template <class TestType>
    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;
    160     UsesAllocatorTestBaseStorage(Alloc const& a) : allocator(a) {}
    161     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 
    172   UsesAllocatorTestBaseStorage() : dummy(), has_alloc(false) {}
    173   UsesAllocatorTestBaseStorage(Alloc const& a) : alloc(a), has_alloc(true) {}
    174   ~UsesAllocatorTestBaseStorage() {
    175       if (has_alloc)
    176           alloc.~Alloc();
    177   }
    178 
    179   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>
    192     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>
    199     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 
    208     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:
    217     explicit UsesAllocatorTestBase(const TypeID* aid)
    218         : args_id(aid), constructor_called(UA_None), alloc_store()
    219     {}
    220 
    221     UsesAllocatorTestBase(UsesAllocatorTestBase const&)
    222         : args_id(&makeArgumentID<Self const&>()), constructor_called(UA_None),
    223           alloc_store()
    224     {}
    225 
    226     UsesAllocatorTestBase(UsesAllocatorTestBase&&)
    227         : args_id(&makeArgumentID<Self&&>()), constructor_called(UA_None),
    228           alloc_store()
    229     {}
    230 
    231     template <class ...Args>
    232     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&&...>>
    239     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>
    250     static CtorAlloc getAllocatorFromPack(ArgumentListID<LArgs...>, Args&&... args) {
    251         return UsesAllocatorTestBase::getAllocatorFromPackImp<LArgs const&...>(args...);
    252     }
    253 
    254     template <class ...LArgs>
    255     static CtorAlloc getAllocatorFromPackImp(
    256         typename detail::Identity<LArgs>::type..., CtorAlloc const& alloc) {
    257         return alloc;
    258     }
    259 
    260     bool has_alloc() const { return alloc_store.get_allocator() != nullptr; }
    261     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 
    277     UsesAllocatorV1() : Base(&makeArgumentID<>()) {}
    278 
    279     UsesAllocatorV1(UsesAllocatorV1 const&)
    280         : Base(&makeArgumentID<UsesAllocatorV1 const&>()) {}
    281     UsesAllocatorV1(UsesAllocatorV1 &&)
    282         : Base(&makeArgumentID<UsesAllocatorV1 &&>()) {}
    283     // Non-Uses Allocator Ctor
    284     template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
    285     UsesAllocatorV1(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
    286 
    287     // Uses Allocator Arg Ctor
    288     template <class ...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>
    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 
    311     UsesAllocatorV2() : Base(&makeArgumentID<>()) {}
    312     UsesAllocatorV2(UsesAllocatorV2 const&)
    313         : Base(&makeArgumentID<UsesAllocatorV2 const&>()) {}
    314     UsesAllocatorV2(UsesAllocatorV2 &&)
    315         : Base(&makeArgumentID<UsesAllocatorV2 &&>()) {}
    316 
    317     // Non-Uses Allocator Ctor
    318     template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
    319     UsesAllocatorV2(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
    320 
    321     // Uses Allocator Last Ctor
    322     template <class ...Args, EnableIfB<sizeof...(Args) == Arity + 1> = false>
    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 
    337     UsesAllocatorV3() : Base(&makeArgumentID<>()) {}
    338     UsesAllocatorV3(UsesAllocatorV3 const&)
    339         : Base(&makeArgumentID<UsesAllocatorV3 const&>()) {}
    340     UsesAllocatorV3(UsesAllocatorV3 &&)
    341         : Base(&makeArgumentID<UsesAllocatorV3 &&>()) {}
    342 
    343     // Non-Uses Allocator Ctor
    344     template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
    345     UsesAllocatorV3(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
    346 
    347     // Uses Allocator Arg Ctor
    348     template <class ...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>
    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 
    369     NotUsesAllocator() : Base(&makeArgumentID<>()) {}
    370     NotUsesAllocator(NotUsesAllocator const&)
    371         : Base(&makeArgumentID<NotUsesAllocator const&>()) {}
    372     NotUsesAllocator(NotUsesAllocator &&)
    373         : Base(&makeArgumentID<NotUsesAllocator &&>()) {}
    374     // Non-Uses Allocator Ctor
    375     template <class ...Args, EnableIfB<sizeof...(Args) == Arity> = false>
    376     NotUsesAllocator(Args&&...) : Base(&makeArgumentID<Args&&...>()) {}
    377 
    378     // Uses Allocator Arg Ctor
    379     template <class ...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>
    386     NotUsesAllocator(Args&&... args)
    387         : Base(AllocLastTag{}, std::forward<Args>(args)...)
    388     {}
    389 };
    390 
    391 #endif /* USES_ALLOC_TYPES_HPP */
    392