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 __movable_box. See the documentation 20 // in __movable_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 // Copy constructible and movable, but not copyable. This uses the primary template, however we're 37 // still able to use the native move-assignment operator in this case. 38 struct CopyConstructibleMovable { 39 constexpr CopyConstructibleMovable() = default; CopyConstructibleMovableCopyConstructibleMovable40 constexpr explicit CopyConstructibleMovable(int x) : value(x) {} 41 CopyConstructibleMovable(CopyConstructibleMovable const&) noexcept(false) = default; 42 CopyConstructibleMovable(CopyConstructibleMovable&&) noexcept(false) = default; 43 CopyConstructibleMovable& operator=(CopyConstructibleMovable const&) = delete; 44 45 constexpr CopyConstructibleMovable& operator=(CopyConstructibleMovable&& other) { 46 value = other.value; 47 did_move_assign = true; 48 return *this; 49 } 50 51 int value = -1; 52 bool did_move_assign = false; 53 }; 54 55 // Copyable type that is not nothrow_copy/move_constructible. 56 // This triggers optimization #1 for the copy assignment and the move assignment. 57 struct Copyable { 58 constexpr Copyable() = default; CopyableCopyable59 constexpr explicit Copyable(int x) : value(x) {} 60 Copyable(Copyable const&) noexcept(false) = default; 61 noexceptCopyable62 constexpr Copyable& operator=(Copyable const& other) noexcept(false) { 63 value = other.value; 64 did_copy_assign = true; 65 return *this; 66 } 67 noexceptCopyable68 constexpr Copyable& operator=(Copyable&& other) noexcept(false) { 69 value = other.value; 70 did_move_assign = true; 71 return *this; 72 } 73 74 int value = -1; 75 bool did_copy_assign = false; 76 bool did_move_assign = false; 77 }; 78 static_assert(std::copyable<Copyable>); 79 static_assert(!std::is_nothrow_copy_constructible_v<Copyable>); 80 static_assert(std::movable<Copyable>); 81 static_assert(!std::is_nothrow_move_constructible_v<Copyable>); 82 83 // Non-copyable type that is nothrow_copy_constructible and nothrow_move_constructible. 84 // This triggers optimization #2 for the copy assignment and the move assignment. 85 struct NothrowCopyConstructible { 86 constexpr NothrowCopyConstructible() = default; NothrowCopyConstructibleNothrowCopyConstructible87 constexpr explicit NothrowCopyConstructible(int x) : value(x) {} 88 NothrowCopyConstructible(NothrowCopyConstructible const&) noexcept = default; 89 NothrowCopyConstructible(NothrowCopyConstructible&&) noexcept = default; 90 NothrowCopyConstructible& operator=(NothrowCopyConstructible const&) = delete; 91 92 int value = -1; 93 }; 94 static_assert(!std::copyable<NothrowCopyConstructible>); 95 static_assert(std::is_nothrow_copy_constructible_v<NothrowCopyConstructible>); 96 static_assert(!std::movable<NothrowCopyConstructible>); 97 static_assert(std::is_nothrow_move_constructible_v<NothrowCopyConstructible>); 98 99 // Non-copyable type that is nothrow_copy_constructible, and that is movable but NOT nothrow_move_constructible. 100 // This triggers optimization #2 for the copy assignment, and optimization #1 for the move assignment. 101 struct MovableNothrowCopyConstructible { 102 constexpr MovableNothrowCopyConstructible() = default; MovableNothrowCopyConstructibleMovableNothrowCopyConstructible103 constexpr explicit MovableNothrowCopyConstructible(int x) : value(x) {} 104 MovableNothrowCopyConstructible(MovableNothrowCopyConstructible const&) noexcept = default; 105 MovableNothrowCopyConstructible(MovableNothrowCopyConstructible&&) noexcept(false) = default; 106 constexpr MovableNothrowCopyConstructible& operator=(MovableNothrowCopyConstructible&& other) { 107 value = other.value; 108 did_move_assign = true; 109 return *this; 110 } 111 112 int value = -1; 113 bool did_move_assign = false; 114 }; 115 static_assert(!std::copyable<MovableNothrowCopyConstructible>); 116 static_assert(std::is_nothrow_copy_constructible_v<MovableNothrowCopyConstructible>); 117 static_assert(std::movable<MovableNothrowCopyConstructible>); 118 static_assert(!std::is_nothrow_move_constructible_v<MovableNothrowCopyConstructible>); 119 120 #if !defined(TEST_HAS_NO_EXCEPTIONS) 121 // A type that we can make throw when copied from. This is used to create a 122 // copyable-box in the empty state. 123 static constexpr int THROW_WHEN_COPIED_FROM = 999; 124 struct ThrowsOnCopy { 125 constexpr ThrowsOnCopy() = default; ThrowsOnCopyThrowsOnCopy126 constexpr explicit ThrowsOnCopy(int x) : value(x) {} ThrowsOnCopyThrowsOnCopy127 ThrowsOnCopy(ThrowsOnCopy const& other) { 128 if (other.value == THROW_WHEN_COPIED_FROM) 129 throw 0; 130 else 131 value = other.value; 132 } 133 134 ThrowsOnCopy& operator=(ThrowsOnCopy const&) = delete; // prevent from being copyable 135 136 int value = -1; 137 }; 138 139 // Creates an empty box. The only way to do that is to try assigning one box 140 // to another and have that fail due to an exception when calling the copy 141 // constructor. The assigned-to box will then be in the empty state. create_empty_box()142inline std::ranges::__movable_box<ThrowsOnCopy> create_empty_box() { 143 std::ranges::__movable_box<ThrowsOnCopy> box1; 144 std::ranges::__movable_box<ThrowsOnCopy> box2(std::in_place, THROW_WHEN_COPIED_FROM); 145 try { 146 box1 = box2; // throws during assignment, which is implemented as a call to the copy ctor 147 } catch (...) { 148 // now, box1 is empty 149 assert(!box1.__has_value()); 150 return box1; 151 } 152 assert(false && "should never be reached"); 153 return box1; // to silence warning about missing return in non-void function 154 } 155 #endif // !defined(TEST_HAS_NO_EXCEPTIONS) 156 157 #endif // TEST_LIBCXX_RANGES_RANGE_ADAPTORS_RANGE_COPY_WRAP_TYPES_H 158