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