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_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_COPY_WRAP_TYPES_H 10 #define TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_COPY_WRAP_TYPES_H 11 12 #include <ranges> 13 #include <cassert> 14 #include <concepts> 15 #include <type_traits> 16 17 #include "test_macros.h" 18 19 // NOTE: These types are strongly tied to the implementation of __copyable_box. See the documentation 20 // in __copyable_box for the meaning of optimizations #1 and #2. 21 22 // Copy constructible, but neither copyable nor nothrow_copy/move_constructible. This uses the primary template. 23 struct CopyConstructible { 24 constexpr CopyConstructible() = default; CopyConstructibleCopyConstructible25 constexpr explicit CopyConstructible(int x) : value(x) { } 26 CopyConstructible(CopyConstructible const&) noexcept(false) = default; 27 CopyConstructible& operator=(CopyConstructible const&) = delete; 28 29 int value = -1; 30 }; 31 static_assert(!std::copyable<CopyConstructible>); 32 static_assert(!std::is_nothrow_copy_constructible_v<CopyConstructible>); 33 static_assert(!std::movable<CopyConstructible>); 34 static_assert(!std::is_nothrow_move_constructible_v<CopyConstructible>); 35 36 37 // Copy constructible and movable, but not copyable. This uses the primary template, however we're 38 // still able to use the native move-assignment operator in this case. 39 struct CopyConstructibleMovable { 40 constexpr CopyConstructibleMovable() = default; CopyConstructibleMovableCopyConstructibleMovable41 constexpr explicit CopyConstructibleMovable(int x) : value(x) { } 42 CopyConstructibleMovable(CopyConstructibleMovable const&) noexcept(false) = default; 43 CopyConstructibleMovable(CopyConstructibleMovable&&) noexcept(false) = default; 44 CopyConstructibleMovable& operator=(CopyConstructibleMovable const&) = delete; 45 46 constexpr CopyConstructibleMovable& operator=(CopyConstructibleMovable&& other) { 47 value = other.value; 48 did_move_assign = true; 49 return *this; 50 } 51 52 int value = -1; 53 bool did_move_assign = false; 54 }; 55 56 57 // Copyable type that is not nothrow_copy/move_constructible. 58 // This triggers optimization #1 for the copy assignment and the move assignment. 59 struct Copyable { 60 constexpr Copyable() = default; CopyableCopyable61 constexpr explicit Copyable(int x) : value(x) { } 62 Copyable(Copyable const&) noexcept(false) = default; 63 noexceptCopyable64 constexpr Copyable& operator=(Copyable const& other) noexcept(false) { 65 value = other.value; 66 did_copy_assign = true; 67 return *this; 68 } 69 noexceptCopyable70 constexpr Copyable& operator=(Copyable&& other) noexcept(false) { 71 value = other.value; 72 did_move_assign = true; 73 return *this; 74 } 75 76 int value = -1; 77 bool did_copy_assign = false; 78 bool did_move_assign = false; 79 }; 80 static_assert( std::copyable<Copyable>); 81 static_assert(!std::is_nothrow_copy_constructible_v<Copyable>); 82 static_assert( std::movable<Copyable>); 83 static_assert(!std::is_nothrow_move_constructible_v<Copyable>); 84 85 86 // Non-copyable type that is nothrow_copy_constructible and nothrow_move_constructible. 87 // This triggers optimization #2 for the copy assignment and the move assignment. 88 struct NothrowCopyConstructible { 89 constexpr NothrowCopyConstructible() = default; NothrowCopyConstructibleNothrowCopyConstructible90 constexpr explicit NothrowCopyConstructible(int x) : value(x) { } 91 NothrowCopyConstructible(NothrowCopyConstructible const&) noexcept = default; 92 NothrowCopyConstructible(NothrowCopyConstructible&&) noexcept = default; 93 NothrowCopyConstructible& operator=(NothrowCopyConstructible const&) = delete; 94 95 int value = -1; 96 }; 97 static_assert(!std::copyable<NothrowCopyConstructible>); 98 static_assert( std::is_nothrow_copy_constructible_v<NothrowCopyConstructible>); 99 static_assert(!std::movable<NothrowCopyConstructible>); 100 static_assert( std::is_nothrow_move_constructible_v<NothrowCopyConstructible>); 101 102 103 // Non-copyable type that is nothrow_copy_constructible, and that is movable but NOT nothrow_move_constructible. 104 // This triggers optimization #2 for the copy assignment, and optimization #1 for the move assignment. 105 struct MovableNothrowCopyConstructible { 106 constexpr MovableNothrowCopyConstructible() = default; MovableNothrowCopyConstructibleMovableNothrowCopyConstructible107 constexpr explicit MovableNothrowCopyConstructible(int x) : value(x) { } 108 MovableNothrowCopyConstructible(MovableNothrowCopyConstructible const&) noexcept = default; 109 MovableNothrowCopyConstructible(MovableNothrowCopyConstructible&&) noexcept(false) = default; 110 constexpr MovableNothrowCopyConstructible& operator=(MovableNothrowCopyConstructible&& other) { 111 value = other.value; 112 did_move_assign = true; 113 return *this; 114 } 115 116 int value = -1; 117 bool did_move_assign = false; 118 }; 119 static_assert(!std::copyable<MovableNothrowCopyConstructible>); 120 static_assert( std::is_nothrow_copy_constructible_v<MovableNothrowCopyConstructible>); 121 static_assert( std::movable<MovableNothrowCopyConstructible>); 122 static_assert(!std::is_nothrow_move_constructible_v<MovableNothrowCopyConstructible>); 123 124 125 #if !defined(TEST_HAS_NO_EXCEPTIONS) 126 // A type that we can make throw when copied from. This is used to create a 127 // copyable-box in the empty state. 128 static constexpr int THROW_WHEN_COPIED_FROM = 999; 129 struct ThrowsOnCopy { 130 constexpr ThrowsOnCopy() = default; ThrowsOnCopyThrowsOnCopy131 constexpr explicit ThrowsOnCopy(int x) : value(x) { } ThrowsOnCopyThrowsOnCopy132 ThrowsOnCopy(ThrowsOnCopy const& other) { 133 if (other.value == THROW_WHEN_COPIED_FROM) throw 0; 134 else value = other.value; 135 } 136 137 ThrowsOnCopy& operator=(ThrowsOnCopy const&) = delete; // prevent from being copyable 138 139 int value = -1; 140 }; 141 142 // Creates an empty box. The only way to do that is to try assigning one box 143 // to another and have that fail due to an exception when calling the copy 144 // constructor. The assigned-to box will then be in the empty state. create_empty_box()145inline std::ranges::__copyable_box<ThrowsOnCopy> create_empty_box() { 146 std::ranges::__copyable_box<ThrowsOnCopy> box1; 147 std::ranges::__copyable_box<ThrowsOnCopy> box2(std::in_place, THROW_WHEN_COPIED_FROM); 148 try { 149 box1 = box2; // throws during assignment, which is implemented as a call to the copy ctor 150 } catch (...) { 151 // now, box1 is empty 152 assert(!box1.__has_value()); 153 return box1; 154 } 155 assert(false && "should never be reached"); 156 return box1; // to silence warning about missing return in non-void function 157 } 158 #endif // !defined(TEST_HAS_NO_EXCEPTIONS) 159 160 #endif // TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_COPY_WRAP_TYPES_H 161