• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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()145 inline 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