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