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
17template <bool copyMoveNoexcept, bool convertNoexcept = true>
18struct 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
68using Traced = TracedBase<false>;
69using TracedNoexcept = TracedBase<true>;
70
71using MoveThrowConvNoexcept = TracedBase<false, true>;
72using MoveNoexceptConvThrow = TracedBase<true, false>;
73using BothMayThrow = TracedBase<false, false>;
74using BothNoexcept = TracedBase<true, true>;
75
76struct 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(a&: x.i, b&: y.i);
82 x.adlSwapCalled = true;
83 y.adlSwapCalled = true;
84 }
85};
86
87template <bool Noexcept>
88struct 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
108struct Except {};
109
110struct ThrowOnCopyConstruct {
111 ThrowOnCopyConstruct() = default;
112 ThrowOnCopyConstruct(const ThrowOnCopyConstruct&) { throw Except{}; }
113 ThrowOnCopyConstruct& operator=(const ThrowOnCopyConstruct&) = default;
114};
115
116struct ThrowOnMoveConstruct {
117 ThrowOnMoveConstruct() = default;
118 ThrowOnMoveConstruct(ThrowOnMoveConstruct&&) { throw Except{}; }
119 ThrowOnMoveConstruct& operator=(ThrowOnMoveConstruct&&) = default;
120};
121
122struct 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
132struct 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
147struct 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.
161template <int Constant>
162struct TailClobberer {
163 constexpr TailClobberer() noexcept {
164 if (!std::is_constant_evaluated()) {
165 std::memset(s: this, c: Constant, n: 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
180private:
181 alignas(2) bool b;
182};
183static_assert(!std::is_trivially_copy_constructible_v<TailClobberer<0>>);
184static_assert(std::is_trivially_move_constructible_v<TailClobberer<0>>);
185
186template <int Constant, bool Noexcept = true, bool ThrowOnMove = false>
187struct 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};
196static_assert(!std::is_trivially_copy_constructible_v<TailClobbererNonTrivialMove<0>>);
197static_assert(std::is_move_constructible_v<TailClobbererNonTrivialMove<0>>);
198static_assert(!std::is_trivially_move_constructible_v<TailClobbererNonTrivialMove<0>>);
199static_assert(std::is_nothrow_move_constructible_v<TailClobbererNonTrivialMove<0, true>>);
200static_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
244struct BoolWithPadding {
245 constexpr explicit BoolWithPadding() noexcept : BoolWithPadding(false) {}
246 constexpr BoolWithPadding(bool val) noexcept {
247 if (!std::is_constant_evaluated()) {
248 std::memset(s: this, c: 0, n: 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
263private:
264 alignas(8) bool val_;
265};
266
267struct IntWithoutPadding {
268 constexpr explicit IntWithoutPadding() noexcept : IntWithoutPadding(0) {}
269 constexpr IntWithoutPadding(int val) noexcept {
270 if (!std::is_constant_evaluated()) {
271 std::memset(s: this, c: 0, n: 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
283private:
284 int val_;
285};
286
287template <bool WithPaddedExpected, bool ExpectedVoid>
288struct CheckForInvalidWritesBaseImpl;
289template <>
290struct CheckForInvalidWritesBaseImpl<true, false> {
291 using type = std::expected<IntWithoutPadding, bool>;
292};
293template <>
294struct CheckForInvalidWritesBaseImpl<false, false> {
295 using type = std::expected<BoolWithPadding, bool>;
296};
297template <>
298struct CheckForInvalidWritesBaseImpl<true, true> {
299 using type = std::expected<void, IntWithoutPadding>;
300};
301template <>
302struct CheckForInvalidWritesBaseImpl<false, true> {
303 using type = std::expected<void, BoolWithPadding>;
304};
305
306template <bool WithPaddedExpected, bool ExpectedVoid>
307using CheckForInvalidWritesBase = typename CheckForInvalidWritesBaseImpl<WithPaddedExpected, ExpectedVoid>::type;
308
309template <bool WithPaddedExpected, bool ExpectedVoid = false>
310struct 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

source code of libcxx/test/std/utilities/expected/types.h