Skip to content

Commit b54f8a1

Browse files
committed
[libc++] Make constexpr std::variant. Implement P2231R1
1 parent 6d13263 commit b54f8a1

File tree

15 files changed

+1304
-828
lines changed

15 files changed

+1304
-828
lines changed

libcxx/docs/ReleaseNotes/19.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ Implemented Papers
4444
- P2495R3 - Interfacing ``stringstream``\s with ``string_view``
4545
- P2302R4 - ``std::ranges::contains``
4646
- P1659R3 - ``std::ranges::starts_with`` and ``std::ranges::ends_with``
47+
- P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``
48+
4749

4850
Improvements and New Features
4951
-----------------------------

libcxx/docs/Status/Cxx20.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ Paper Status
4747
.. [#note-P0619] P0619: Only sections D.8, D.9, D.10 and D.13 are implemented. Sections D.4, D.7, D.11, and D.12 remain undone.
4848
.. [#note-P0883.1] P0883: shared_ptr and floating-point changes weren't applied as they themselves aren't implemented yet.
4949
.. [#note-P0883.2] P0883: ``ATOMIC_FLAG_INIT`` was marked deprecated in version 14.0, but was undeprecated with the implementation of LWG3659 in version 15.0.
50-
.. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet.
5150
.. [#note-P0660] P0660: The paper is implemented but the features are experimental and can be enabled via ``-fexperimental-library``.
5251
.. [#note-P0355] P0355: The implementation status is:
5352

libcxx/docs/Status/Cxx20Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192
"`P2106R0 <https://wg21.link/P2106R0>`__","LWG","Alternative wording for GB315 and GB316","Prague","|Complete|","15.0","|ranges|"
193193
"`P2116R0 <https://wg21.link/P2116R0>`__","LWG","Remove tuple-like protocol support from fixed-extent span","Prague","|Complete|","11.0"
194194
"","","","","","",""
195-
"`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Partial| [#note-P2231]_","13.0"
195+
"`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Complete|","19.0"
196196
"`P2325R3 <https://wg21.link/P2325R3>`__","LWG","Views should not be required to be default constructible","June 2021","|Complete|","16.0","|ranges|"
197197
"`P2210R2 <https://wg21.link/P2210R2>`__","LWG","Superior String Splitting","June 2021","|Complete|","16.0","|ranges|"
198198
"`P2216R3 <https://wg21.link/P2216R3>`__","LWG","std::format improvements","June 2021","|Complete|","15.0"

libcxx/include/variant

Lines changed: 115 additions & 96 deletions
Large diffs are not rendered by default.

libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp

Lines changed: 173 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ struct Dummy {
3333

3434
struct ThrowsCtorT {
3535
ThrowsCtorT(int) noexcept(false) {}
36-
ThrowsCtorT &operator=(int) noexcept { return *this; }
36+
ThrowsCtorT& operator=(int) noexcept { return *this; }
3737
};
3838

3939
struct ThrowsAssignT {
4040
ThrowsAssignT(int) noexcept {}
41-
ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
41+
ThrowsAssignT& operator=(int) noexcept(false) { return *this; }
4242
};
4343

4444
struct NoThrowT {
4545
NoThrowT(int) noexcept {}
46-
NoThrowT &operator=(int) noexcept { return *this; }
46+
NoThrowT& operator=(int) noexcept { return *this; }
4747
};
4848

4949
} // namespace MetaHelpers
@@ -55,7 +55,7 @@ struct ThrowsCtorT {
5555
int value;
5656
ThrowsCtorT() : value(0) {}
5757
ThrowsCtorT(int) noexcept(false) { throw 42; }
58-
ThrowsCtorT &operator=(int v) noexcept {
58+
ThrowsCtorT& operator=(int v) noexcept {
5959
value = v;
6060
return *this;
6161
}
@@ -64,9 +64,12 @@ struct ThrowsCtorT {
6464
struct MoveCrashes {
6565
int value;
6666
MoveCrashes(int v = 0) noexcept : value{v} {}
67-
MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
68-
MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
69-
MoveCrashes &operator=(int v) noexcept {
67+
MoveCrashes(MoveCrashes&&) noexcept { assert(false); }
68+
MoveCrashes& operator=(MoveCrashes&&) noexcept {
69+
assert(false);
70+
return *this;
71+
}
72+
MoveCrashes& operator=(int v) noexcept {
7073
value = v;
7174
return *this;
7275
}
@@ -76,8 +79,8 @@ struct ThrowsCtorTandMove {
7679
int value;
7780
ThrowsCtorTandMove() : value(0) {}
7881
ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
79-
ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
80-
ThrowsCtorTandMove &operator=(int v) noexcept {
82+
ThrowsCtorTandMove(ThrowsCtorTandMove&&) noexcept(false) { assert(false); }
83+
ThrowsCtorTandMove& operator=(int v) noexcept {
8184
value = v;
8285
return *this;
8386
}
@@ -87,14 +90,14 @@ struct ThrowsAssignT {
8790
int value;
8891
ThrowsAssignT() : value(0) {}
8992
ThrowsAssignT(int v) noexcept : value(v) {}
90-
ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
93+
ThrowsAssignT& operator=(int) noexcept(false) { throw 42; }
9194
};
9295

9396
struct NoThrowT {
9497
int value;
9598
NoThrowT() : value(0) {}
9699
NoThrowT(int v) noexcept : value(v) {}
97-
NoThrowT &operator=(int v) noexcept {
100+
NoThrowT& operator=(int v) noexcept {
98101
value = v;
99102
return *this;
100103
}
@@ -103,7 +106,7 @@ struct NoThrowT {
103106
#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
104107
} // namespace RuntimeHelpers
105108

106-
void test_T_assignment_noexcept() {
109+
constexpr void test_T_assignment_noexcept() {
107110
using namespace MetaHelpers;
108111
{
109112
using V = std::variant<Dummy, NoThrowT>;
@@ -119,17 +122,17 @@ void test_T_assignment_noexcept() {
119122
}
120123
}
121124

122-
void test_T_assignment_sfinae() {
125+
constexpr void test_T_assignment_sfinae() {
123126
{
124127
using V = std::variant<long, long long>;
125128
static_assert(!std::is_assignable<V, int>::value, "ambiguous");
126129
}
127130
{
128131
using V = std::variant<std::string, std::string>;
129-
static_assert(!std::is_assignable<V, const char *>::value, "ambiguous");
132+
static_assert(!std::is_assignable<V, const char*>::value, "ambiguous");
130133
}
131134
{
132-
using V = std::variant<std::string, void *>;
135+
using V = std::variant<std::string, void*>;
133136
static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
134137
}
135138
{
@@ -138,8 +141,7 @@ void test_T_assignment_sfinae() {
138141
}
139142
{
140143
using V = std::variant<std::unique_ptr<int>, bool>;
141-
static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value,
142-
"no explicit bool in operator=");
144+
static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value, "no explicit bool in operator=");
143145
struct X {
144146
operator void*();
145147
};
@@ -152,12 +154,11 @@ void test_T_assignment_sfinae() {
152154
operator X();
153155
};
154156
using V = std::variant<X>;
155-
static_assert(std::is_assignable<V, Y>::value,
156-
"regression on user-defined conversions in operator=");
157+
static_assert(std::is_assignable<V, Y>::value, "regression on user-defined conversions in operator=");
157158
}
158159
}
159160

160-
void test_T_assignment_basic() {
161+
TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() {
161162
{
162163
std::variant<int> v(43);
163164
v = 42;
@@ -184,19 +185,146 @@ void test_T_assignment_basic() {
184185
}
185186
{
186187
std::variant<std::string, bool> v = true;
187-
v = "bar";
188+
v = "bar";
188189
assert(v.index() == 0);
189190
assert(std::get<0>(v) == "bar");
190191
}
192+
}
193+
194+
void test_T_assignment_basic_no_constexpr() {
195+
std::variant<bool, std::unique_ptr<int>> v;
196+
v = nullptr;
197+
assert(v.index() == 1);
198+
assert(std::get<1>(v) == nullptr);
199+
}
200+
201+
struct TraceStat {
202+
int construct = 0;
203+
int copy_construct = 0;
204+
int copy_assign = 0;
205+
int move_construct = 0;
206+
int move_assign = 0;
207+
int T_copy_assign = 0;
208+
int T_move_assign = 0;
209+
int destroy = 0;
210+
};
211+
212+
template <bool CtorNoexcept, bool MoveCtorNoexcept>
213+
struct Trace {
214+
struct T {};
215+
216+
constexpr Trace(TraceStat* s) noexcept(CtorNoexcept) : stat(s) { ++s->construct; }
217+
constexpr Trace(T) noexcept(CtorNoexcept) : stat(nullptr) {}
218+
constexpr Trace(const Trace& o) : stat(o.stat) { ++stat->copy_construct; }
219+
constexpr Trace(Trace&& o) noexcept(MoveCtorNoexcept) : stat(o.stat) { ++stat->move_construct; }
220+
constexpr Trace& operator=(const Trace&) {
221+
++stat->copy_assign;
222+
return *this;
223+
}
224+
constexpr Trace& operator=(Trace&&) noexcept {
225+
++stat->move_assign;
226+
return *this;
227+
}
228+
229+
constexpr Trace& operator=(const T&) {
230+
++stat->T_copy_assign;
231+
return *this;
232+
}
233+
constexpr Trace& operator=(T&&) noexcept {
234+
++stat->T_move_assign;
235+
return *this;
236+
}
237+
TEST_CONSTEXPR_CXX20 ~Trace() { ++stat->destroy; }
238+
239+
TraceStat* stat;
240+
};
241+
242+
TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_construction() {
191243
{
192-
std::variant<bool, std::unique_ptr<int>> v;
193-
v = nullptr;
194-
assert(v.index() == 1);
195-
assert(std::get<1>(v) == nullptr);
244+
using V = std::variant<int, Trace<false, false>>;
245+
TraceStat stat;
246+
V v{1};
247+
v = &stat;
248+
assert(stat.construct == 1);
249+
assert(stat.copy_construct == 0);
250+
assert(stat.move_construct == 0);
251+
assert(stat.copy_assign == 0);
252+
assert(stat.move_assign == 0);
253+
assert(stat.destroy == 0);
254+
}
255+
{
256+
using V = std::variant<int, Trace<false, true>>;
257+
TraceStat stat;
258+
V v{1};
259+
v = &stat;
260+
assert(stat.construct == 1);
261+
assert(stat.copy_construct == 0);
262+
assert(stat.move_construct == 1);
263+
assert(stat.copy_assign == 0);
264+
assert(stat.move_assign == 0);
265+
assert(stat.destroy == 1);
266+
}
267+
268+
{
269+
using V = std::variant<int, Trace<true, false>>;
270+
TraceStat stat;
271+
V v{1};
272+
v = &stat;
273+
assert(stat.construct == 1);
274+
assert(stat.copy_construct == 0);
275+
assert(stat.move_construct == 0);
276+
assert(stat.copy_assign == 0);
277+
assert(stat.move_assign == 0);
278+
assert(stat.destroy == 0);
279+
}
280+
281+
{
282+
using V = std::variant<int, Trace<true, true>>;
283+
TraceStat stat;
284+
V v{1};
285+
v = &stat;
286+
assert(stat.construct == 1);
287+
assert(stat.copy_construct == 0);
288+
assert(stat.move_construct == 0);
289+
assert(stat.copy_assign == 0);
290+
assert(stat.move_assign == 0);
291+
assert(stat.destroy == 0);
196292
}
197293
}
198294

199-
void test_T_assignment_performs_construction() {
295+
TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_assignment() {
296+
{
297+
using V = std::variant<int, Trace<false, false>>;
298+
TraceStat stat;
299+
V v{&stat};
300+
v = Trace<false, false>::T{};
301+
assert(stat.construct == 1);
302+
assert(stat.copy_construct == 0);
303+
assert(stat.move_construct == 0);
304+
assert(stat.copy_assign == 0);
305+
assert(stat.move_assign == 0);
306+
assert(stat.T_copy_assign == 0);
307+
assert(stat.T_move_assign == 1);
308+
assert(stat.destroy == 0);
309+
}
310+
{
311+
using V = std::variant<int, Trace<false, false>>;
312+
TraceStat stat;
313+
V v{&stat};
314+
Trace<false, false>::T t;
315+
v = t;
316+
assert(stat.construct == 1);
317+
assert(stat.copy_construct == 0);
318+
assert(stat.move_construct == 0);
319+
assert(stat.copy_assign == 0);
320+
assert(stat.move_assign == 0);
321+
assert(stat.T_copy_assign == 1);
322+
assert(stat.T_move_assign == 0);
323+
assert(stat.destroy == 0);
324+
}
325+
}
326+
327+
void test_T_assignment_performs_construction_throw() {
200328
using namespace RuntimeHelpers;
201329
#ifndef TEST_HAS_NO_EXCEPTIONS
202330
{
@@ -220,7 +348,7 @@ void test_T_assignment_performs_construction() {
220348
#endif // TEST_HAS_NO_EXCEPTIONS
221349
}
222350

223-
void test_T_assignment_performs_assignment() {
351+
void test_T_assignment_performs_assignment_throw() {
224352
using namespace RuntimeHelpers;
225353
#ifndef TEST_HAS_NO_EXCEPTIONS
226354
{
@@ -262,21 +390,37 @@ void test_T_assignment_performs_assignment() {
262390
#endif // TEST_HAS_NO_EXCEPTIONS
263391
}
264392

265-
void test_T_assignment_vector_bool() {
393+
TEST_CONSTEXPR_CXX20 void test_T_assignment_vector_bool() {
266394
std::vector<bool> vec = {true};
267395
std::variant<bool, int> v;
268396
v = vec[0];
269397
assert(v.index() == 0);
270398
assert(std::get<0>(v) == true);
271399
}
272400

273-
int main(int, char**) {
401+
void non_constexpr_test() {
402+
test_T_assignment_basic_no_constexpr();
403+
test_T_assignment_performs_construction_throw();
404+
test_T_assignment_performs_assignment_throw();
405+
}
406+
407+
TEST_CONSTEXPR_CXX20 bool test() {
274408
test_T_assignment_basic();
275409
test_T_assignment_performs_construction();
276410
test_T_assignment_performs_assignment();
277411
test_T_assignment_noexcept();
278412
test_T_assignment_sfinae();
279413
test_T_assignment_vector_bool();
280414

415+
return true;
416+
}
417+
418+
int main(int, char**) {
419+
test();
420+
non_constexpr_test();
421+
422+
#if TEST_STD_VER >= 20
423+
static_assert(test());
424+
#endif
281425
return 0;
282426
}

0 commit comments

Comments
 (0)