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 TEST_STD_UTILITIES_EXPECTED_TYPES_H 10 #define TEST_STD_UTILITIES_EXPECTED_TYPES_H 11 12 #include <cstring> 13 #include <utility> 14 #include <type_traits> 15 #include "test_macros.h" 16 17 template <bool copyMoveNoexcept, bool convertNoexcept = true> 18 struct TracedBase { 19 struct state { 20 bool copyCtorCalled = false; 21 bool copyAssignCalled = false; 22 bool moveCtorCalled = false; 23 bool moveAssignCalled = false; 24 bool dtorCalled = false; 25 }; 26 27 state* state_ = nullptr; 28 bool copiedFromInt = false; 29 bool movedFromInt = false; 30 bool copiedFromTmp = false; 31 bool movedFromTmp = false; 32 int data_; 33 34 constexpr TracedBase(const int& ii) noexcept(convertNoexcept) : data_(ii) { copiedFromInt = true; } 35 constexpr TracedBase(int&& ii) noexcept(convertNoexcept) : data_(ii) { movedFromInt = true; } 36 constexpr TracedBase(state& s, int ii) noexcept : state_(&s), data_(ii) {} 37 constexpr TracedBase(const TracedBase& other) noexcept(copyMoveNoexcept) : state_(other.state_), data_(other.data_) { 38 if (state_) { 39 state_->copyCtorCalled = true; 40 } else { 41 copiedFromTmp = true; 42 } 43 } 44 constexpr TracedBase(TracedBase&& other) noexcept(copyMoveNoexcept) : state_(other.state_), data_(other.data_) { 45 if (state_) { 46 state_->moveCtorCalled = true; 47 } else { 48 movedFromTmp = true; 49 } 50 } 51 constexpr TracedBase& operator=(const TracedBase& other) noexcept(copyMoveNoexcept) { 52 data_ = other.data_; 53 state_->copyAssignCalled = true; 54 return *this; 55 } 56 constexpr TracedBase& operator=(TracedBase&& other) noexcept(copyMoveNoexcept) { 57 data_ = other.data_; 58 state_->moveAssignCalled = true; 59 return *this; 60 } 61 constexpr ~TracedBase() { 62 if (state_) { 63 state_->dtorCalled = true; 64 } 65 } 66 }; 67 68 using Traced = TracedBase<false>; 69 using TracedNoexcept = TracedBase<true>; 70 71 using MoveThrowConvNoexcept = TracedBase<false, true>; 72 using MoveNoexceptConvThrow = TracedBase<true, false>; 73 using BothMayThrow = TracedBase<false, false>; 74 using BothNoexcept = TracedBase<true, true>; 75 76 struct ADLSwap { 77 int i; 78 bool adlSwapCalled = false; 79 constexpr ADLSwap(int ii) : i(ii) {} 80 constexpr friend void swap(ADLSwap& x, ADLSwap& y) { 81 std::swap(x.i, y.i); 82 x.adlSwapCalled = true; 83 y.adlSwapCalled = true; 84 } 85 }; 86 87 template <bool Noexcept> 88 struct TrackedMove { 89 int i; 90 int numberOfMoves = 0; 91 bool swapCalled = false; 92 93 constexpr TrackedMove(int ii) : i(ii) {} 94 constexpr TrackedMove(TrackedMove&& other) noexcept(Noexcept) 95 : i(other.i), numberOfMoves(other.numberOfMoves), swapCalled(other.swapCalled) { 96 ++numberOfMoves; 97 } 98 99 constexpr friend void swap(TrackedMove& x, TrackedMove& y) { 100 std::swap(x.i, y.i); 101 std::swap(x.numberOfMoves, y.numberOfMoves); 102 x.swapCalled = true; 103 y.swapCalled = true; 104 } 105 }; 106 107 #ifndef TEST_HAS_NO_EXCEPTIONS 108 struct Except {}; 109 110 struct ThrowOnCopyConstruct { 111 ThrowOnCopyConstruct() = default; 112 ThrowOnCopyConstruct(const ThrowOnCopyConstruct&) { throw Except{}; } 113 ThrowOnCopyConstruct& operator=(const ThrowOnCopyConstruct&) = default; 114 }; 115 116 struct ThrowOnMoveConstruct { 117 ThrowOnMoveConstruct() = default; 118 ThrowOnMoveConstruct(ThrowOnMoveConstruct&&) { throw Except{}; } 119 ThrowOnMoveConstruct& operator=(ThrowOnMoveConstruct&&) = default; 120 }; 121 122 struct ThrowOnConvert { 123 ThrowOnConvert() = default; 124 ThrowOnConvert(const int&) { throw Except{}; } 125 ThrowOnConvert(int&&) { throw Except{}; } 126 ThrowOnConvert(const ThrowOnConvert&) noexcept(false) {} 127 ThrowOnConvert& operator=(const ThrowOnConvert&) = default; 128 ThrowOnConvert(ThrowOnConvert&&) noexcept(false) {} 129 ThrowOnConvert& operator=(ThrowOnConvert&&) = default; 130 }; 131 132 struct ThrowOnMove { 133 bool* destroyed = nullptr; 134 ThrowOnMove() = default; 135 ThrowOnMove(bool& d) : destroyed(&d) {} 136 ThrowOnMove(ThrowOnMove&&) { throw Except{}; }; 137 ThrowOnMove& operator=(ThrowOnMove&&) = default; 138 ~ThrowOnMove() { 139 if (destroyed) { 140 *destroyed = true; 141 } 142 } 143 }; 144 145 #endif // TEST_HAS_NO_EXCEPTIONS 146 147 struct MoveOnlyErrorType { 148 constexpr MoveOnlyErrorType(int) {} 149 MoveOnlyErrorType(MoveOnlyErrorType&&) {} 150 MoveOnlyErrorType(const MoveOnlyErrorType&&) {} 151 MoveOnlyErrorType(const MoveOnlyErrorType&) = delete; 152 MoveOnlyErrorType& operator=(const MoveOnlyErrorType&) = delete; 153 }; 154 155 // This type has one byte of tail padding where `std::expected` may put its 156 // "has value" flag. The constructor will clobber all bytes including the 157 // tail padding. With this type we can check that `std::expected` handles 158 // the case where the "has value" flag is an overlapping subobject correctly. 159 // 160 // See https://github.com/llvm/llvm-project/issues/68552 for details. 161 template <int Constant> 162 struct TailClobberer { 163 constexpr TailClobberer() noexcept { 164 if (!std::is_constant_evaluated()) { 165 std::memset(static_cast<void*>(this), Constant, sizeof(*this)); 166 } 167 // Always set `b` itself to `false` so that the comparison works. 168 b = false; 169 } 170 constexpr TailClobberer(const TailClobberer&) : TailClobberer() {} 171 constexpr TailClobberer(TailClobberer&&) = default; 172 // Converts from `int`/`std::initializer_list<int>, used in some tests. 173 constexpr TailClobberer(int) : TailClobberer() {} 174 constexpr TailClobberer(std::initializer_list<int>) noexcept : TailClobberer() {} 175 176 friend constexpr bool operator==(const TailClobberer&, const TailClobberer&) = default; 177 178 friend constexpr void swap(TailClobberer&, TailClobberer&) {} 179 180 private: 181 alignas(2) bool b; 182 }; 183 static_assert(!std::is_trivially_copy_constructible_v<TailClobberer<0>>); 184 static_assert(std::is_trivially_move_constructible_v<TailClobberer<0>>); 185 186 template <int Constant, bool Noexcept = true, bool ThrowOnMove = false> 187 struct TailClobbererNonTrivialMove : TailClobberer<Constant> { 188 using TailClobberer<Constant>::TailClobberer; 189 constexpr TailClobbererNonTrivialMove(TailClobbererNonTrivialMove&&) noexcept(Noexcept) : TailClobberer<Constant>() { 190 #ifndef TEST_HAS_NO_EXCEPTIONS 191 if constexpr (!Noexcept && ThrowOnMove) 192 throw Except{}; 193 #endif 194 } 195 }; 196 static_assert(!std::is_trivially_copy_constructible_v<TailClobbererNonTrivialMove<0>>); 197 static_assert(std::is_move_constructible_v<TailClobbererNonTrivialMove<0>>); 198 static_assert(!std::is_trivially_move_constructible_v<TailClobbererNonTrivialMove<0>>); 199 static_assert(std::is_nothrow_move_constructible_v<TailClobbererNonTrivialMove<0, true>>); 200 static_assert(!std::is_nothrow_move_constructible_v<TailClobbererNonTrivialMove<0, false>>); 201 202 // The `CheckForInvalidWrites` class recreates situations where other objects 203 // may be placed into a `std::expected`'s tail padding (see 204 // https://github.com/llvm/llvm-project/issues/70494). With a template 205 // parameter `WithPaddedExpected` two cases can be tested: 206 // 207 // 1. The `std::expected<T, E>` itself has padding, because `T`/`E` _don't_ 208 // have tail padding. This is modelled by `CheckForInvalidWrites<true>` 209 // which has a (potential) data layout like this: 210 // 211 // +- `expected`'s "has value" flag 212 // | 213 // | +- `please_dont_overwrite_me` 214 // | | 215 // /---int---\ | /----------^-------\ // 216 // 00 00 00 00 01 01 01 01 01 01 01 01 217 // \--v---/ 218 // | 219 // | 220 // +- `expected`'s tail padding which 221 // gets repurposed by `please_dont_overwrite_me` 222 // 223 // 2. There is tail padding in the union of `T` and `E` which means the 224 // "has value" flag can be put into this tail padding. In this case, the 225 // `std::expected` itself _must not_ have any tail padding as it may get 226 // overwritten on mutating operations such as `emplace()`. This case is 227 // modelled by `CheckForInvalidWrites<false>` with a (potential) data 228 // layout like this: 229 // 230 // +- bool 231 // | +- please_dont_overwrite_me 232 // | +- "has value" flag | 233 // | | /--------^---------\ // 234 // 00 00 00 00 00 00 00 00 01 01 01 01 01 01 01 00 235 // \---padding-----/ | 236 // +- `CheckForInvalidWrites` 237 // padding 238 // 239 // Note that other implementation strategies are viable, including one that 240 // doesn't make use of `[[no_unique_address]]`. But if an implementation uses 241 // the strategy above, it must make sure that those tail padding bytes are not 242 // overwritten improperly on operations such as `emplace()`. 243 244 struct BoolWithPadding { 245 constexpr explicit BoolWithPadding() noexcept : BoolWithPadding(false) {} 246 constexpr BoolWithPadding(bool val) noexcept { 247 if (!std::is_constant_evaluated()) { 248 std::memset(static_cast<void*>(this), 0, sizeof(*this)); 249 } 250 val_ = val; 251 } 252 constexpr BoolWithPadding(const BoolWithPadding& other) noexcept : BoolWithPadding(other.val_) {} 253 constexpr BoolWithPadding& operator=(const BoolWithPadding& other) noexcept { 254 val_ = other.val_; 255 return *this; 256 } 257 // The previous data layout of libc++'s `expected` required `T` to be 258 // trivially move constructible to employ the `[[no_unique_address]]` 259 // optimization. To trigger bugs with the old implementation, make 260 // `BoolWithPadding` trivially move constructible. 261 constexpr BoolWithPadding(BoolWithPadding&&) = default; 262 263 private: 264 alignas(8) bool val_; 265 }; 266 267 struct IntWithoutPadding { 268 constexpr explicit IntWithoutPadding() noexcept : IntWithoutPadding(0) {} 269 constexpr IntWithoutPadding(int val) noexcept { 270 if (!std::is_constant_evaluated()) { 271 std::memset(static_cast<void*>(this), 0, sizeof(*this)); 272 } 273 val_ = val; 274 } 275 constexpr IntWithoutPadding(const IntWithoutPadding& other) noexcept : IntWithoutPadding(other.val_) {} 276 constexpr IntWithoutPadding& operator=(const IntWithoutPadding& other) noexcept { 277 val_ = other.val_; 278 return *this; 279 } 280 // See comment on `BoolWithPadding`. 281 constexpr IntWithoutPadding(IntWithoutPadding&&) = default; 282 283 private: 284 int val_; 285 }; 286 287 template <bool WithPaddedExpected, bool ExpectedVoid> 288 struct CheckForInvalidWritesBaseImpl; 289 template <> 290 struct CheckForInvalidWritesBaseImpl<true, false> { 291 using type = std::expected<IntWithoutPadding, bool>; 292 }; 293 template <> 294 struct CheckForInvalidWritesBaseImpl<false, false> { 295 using type = std::expected<BoolWithPadding, bool>; 296 }; 297 template <> 298 struct CheckForInvalidWritesBaseImpl<true, true> { 299 using type = std::expected<void, IntWithoutPadding>; 300 }; 301 template <> 302 struct CheckForInvalidWritesBaseImpl<false, true> { 303 using type = std::expected<void, BoolWithPadding>; 304 }; 305 306 template <bool WithPaddedExpected, bool ExpectedVoid> 307 using CheckForInvalidWritesBase = typename CheckForInvalidWritesBaseImpl<WithPaddedExpected, ExpectedVoid>::type; 308 309 template <bool WithPaddedExpected, bool ExpectedVoid = false> 310 struct CheckForInvalidWrites : public CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid> { 311 constexpr CheckForInvalidWrites() = default; 312 constexpr CheckForInvalidWrites(std::unexpect_t) 313 : CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid>(std::unexpect) {} 314 315 constexpr CheckForInvalidWrites& operator=(const CheckForInvalidWrites& other) { 316 CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid>::operator=(other); 317 return *this; 318 } 319 320 constexpr CheckForInvalidWrites& operator=(CheckForInvalidWrites&& other) { 321 CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid>::operator=(std::move(other)); 322 return *this; 323 } 324 325 using CheckForInvalidWritesBase<WithPaddedExpected, ExpectedVoid>::operator=; 326 327 const bool please_dont_overwrite_me[7] = {true, true, true, true, true, true, true}; 328 329 constexpr bool check() { 330 for (bool i : please_dont_overwrite_me) { 331 if (!i) { 332 return false; 333 } 334 } 335 return true; 336 } 337 }; 338 339 #endif // TEST_STD_UTILITIES_EXPECTED_TYPES_H 340