xref: /aosp_15_r20/external/cronet/third_party/libc++/src/test/libcxx/ranges/range.adaptors/range.move.wrap/types.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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()142 inline 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