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