Skip to content

[libc++] Make constexpr std::variant. Implement P2231R1 #83335

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/19.rst
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Implemented Papers
- P3029R1 - Better ``mdspan``'s CTAD
- P2387R3 - Pipe support for user-defined range adaptors
- P2713R1 - Escaping improvements in ``std::format``
- P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant``

Improvements and New Features
-----------------------------
Expand Down
1 change: 0 additions & 1 deletion libcxx/docs/Status/Cxx20.rst
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ Paper Status
.. [#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.
.. [#note-P0883.1] P0883: shared_ptr and floating-point changes weren't applied as they themselves aren't implemented yet.
.. [#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.
.. [#note-P2231] P2231: Optional is complete. The changes to variant haven't been implemented yet.
.. [#note-P0660] P0660: The paper is implemented but the features are experimental and can be enabled via ``-fexperimental-library``.
.. [#note-P0355] P0355: The implementation status is:

Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/Cxx20Papers.csv
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@
"`P2106R0 <https://wg21.link/P2106R0>`__","LWG","Alternative wording for GB315 and GB316","Prague","|Complete|","15.0","|ranges|"
"`P2116R0 <https://wg21.link/P2116R0>`__","LWG","Remove tuple-like protocol support from fixed-extent span","Prague","|Complete|","11.0"
"","","","","","",""
"`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Partial| [#note-P2231]_","13.0"
"`P2231R1 <https://wg21.link/P2231R1>`__","LWG","Missing constexpr in std::optional and std::variant","June 2021","|Complete|","19.0"
"`P2325R3 <https://wg21.link/P2325R3>`__","LWG","Views should not be required to be default constructible","June 2021","|Complete|","16.0","|ranges|"
"`P2210R2 <https://wg21.link/P2210R2>`__","LWG","Superior String Splitting","June 2021","|Complete|","16.0","|ranges|"
"`P2216R3 <https://wg21.link/P2216R3>`__","LWG","std::format improvements","June 2021","|Complete|","15.0"
Expand Down
212 changes: 114 additions & 98 deletions libcxx/include/variant

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ struct Dummy {

struct ThrowsCtorT {
ThrowsCtorT(int) noexcept(false) {}
ThrowsCtorT &operator=(int) noexcept { return *this; }
ThrowsCtorT& operator=(int) noexcept { return *this; }
};

struct ThrowsAssignT {
ThrowsAssignT(int) noexcept {}
ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
ThrowsAssignT& operator=(int) noexcept(false) { return *this; }
};

struct NoThrowT {
NoThrowT(int) noexcept {}
NoThrowT &operator=(int) noexcept { return *this; }
NoThrowT& operator=(int) noexcept { return *this; }
};

} // namespace MetaHelpers
Expand All @@ -55,7 +55,7 @@ struct ThrowsCtorT {
int value;
ThrowsCtorT() : value(0) {}
ThrowsCtorT(int) noexcept(false) { throw 42; }
ThrowsCtorT &operator=(int v) noexcept {
ThrowsCtorT& operator=(int v) noexcept {
value = v;
return *this;
}
Expand All @@ -64,9 +64,12 @@ struct ThrowsCtorT {
struct MoveCrashes {
int value;
MoveCrashes(int v = 0) noexcept : value{v} {}
MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
MoveCrashes &operator=(int v) noexcept {
MoveCrashes(MoveCrashes&&) noexcept { assert(false); }
MoveCrashes& operator=(MoveCrashes&&) noexcept {
assert(false);
return *this;
}
MoveCrashes& operator=(int v) noexcept {
value = v;
return *this;
}
Expand All @@ -76,8 +79,8 @@ struct ThrowsCtorTandMove {
int value;
ThrowsCtorTandMove() : value(0) {}
ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
ThrowsCtorTandMove &operator=(int v) noexcept {
ThrowsCtorTandMove(ThrowsCtorTandMove&&) noexcept(false) { assert(false); }
ThrowsCtorTandMove& operator=(int v) noexcept {
value = v;
return *this;
}
Expand All @@ -87,14 +90,14 @@ struct ThrowsAssignT {
int value;
ThrowsAssignT() : value(0) {}
ThrowsAssignT(int v) noexcept : value(v) {}
ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
ThrowsAssignT& operator=(int) noexcept(false) { throw 42; }
};

struct NoThrowT {
int value;
NoThrowT() : value(0) {}
NoThrowT(int v) noexcept : value(v) {}
NoThrowT &operator=(int v) noexcept {
NoThrowT& operator=(int v) noexcept {
value = v;
return *this;
}
Expand All @@ -103,7 +106,7 @@ struct NoThrowT {
#endif // !defined(TEST_HAS_NO_EXCEPTIONS)
} // namespace RuntimeHelpers

void test_T_assignment_noexcept() {
constexpr void test_T_assignment_noexcept() {
using namespace MetaHelpers;
{
using V = std::variant<Dummy, NoThrowT>;
Expand All @@ -119,17 +122,17 @@ void test_T_assignment_noexcept() {
}
}

void test_T_assignment_sfinae() {
constexpr void test_T_assignment_sfinae() {
{
using V = std::variant<long, long long>;
static_assert(!std::is_assignable<V, int>::value, "ambiguous");
}
{
using V = std::variant<std::string, std::string>;
static_assert(!std::is_assignable<V, const char *>::value, "ambiguous");
static_assert(!std::is_assignable<V, const char*>::value, "ambiguous");
}
{
using V = std::variant<std::string, void *>;
using V = std::variant<std::string, void*>;
static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
}
{
Expand All @@ -138,8 +141,7 @@ void test_T_assignment_sfinae() {
}
{
using V = std::variant<std::unique_ptr<int>, bool>;
static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value,
"no explicit bool in operator=");
static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value, "no explicit bool in operator=");
struct X {
operator void*();
};
Expand All @@ -152,12 +154,11 @@ void test_T_assignment_sfinae() {
operator X();
};
using V = std::variant<X>;
static_assert(std::is_assignable<V, Y>::value,
"regression on user-defined conversions in operator=");
static_assert(std::is_assignable<V, Y>::value, "regression on user-defined conversions in operator=");
}
}

void test_T_assignment_basic() {
TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() {
{
std::variant<int> v(43);
v = 42;
Expand All @@ -184,19 +185,146 @@ void test_T_assignment_basic() {
}
{
std::variant<std::string, bool> v = true;
v = "bar";
v = "bar";
assert(v.index() == 0);
assert(std::get<0>(v) == "bar");
}
}

void test_T_assignment_basic_no_constexpr() {
std::variant<bool, std::unique_ptr<int>> v;
v = nullptr;
assert(v.index() == 1);
assert(std::get<1>(v) == nullptr);
}

struct TraceStat {
int construct = 0;
int copy_construct = 0;
int copy_assign = 0;
int move_construct = 0;
int move_assign = 0;
int T_copy_assign = 0;
int T_move_assign = 0;
int destroy = 0;
};

template <bool CtorNoexcept, bool MoveCtorNoexcept>
struct Trace {
struct T {};

constexpr Trace(TraceStat* s) noexcept(CtorNoexcept) : stat(s) { ++s->construct; }
constexpr Trace(T) noexcept(CtorNoexcept) : stat(nullptr) {}
constexpr Trace(const Trace& o) : stat(o.stat) { ++stat->copy_construct; }
constexpr Trace(Trace&& o) noexcept(MoveCtorNoexcept) : stat(o.stat) { ++stat->move_construct; }
constexpr Trace& operator=(const Trace&) {
++stat->copy_assign;
return *this;
}
constexpr Trace& operator=(Trace&&) noexcept {
++stat->move_assign;
return *this;
}

constexpr Trace& operator=(const T&) {
++stat->T_copy_assign;
return *this;
}
constexpr Trace& operator=(T&&) noexcept {
++stat->T_move_assign;
return *this;
}
TEST_CONSTEXPR_CXX20 ~Trace() { ++stat->destroy; }

TraceStat* stat;
};

TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_construction() {
{
std::variant<bool, std::unique_ptr<int>> v;
v = nullptr;
assert(v.index() == 1);
assert(std::get<1>(v) == nullptr);
using V = std::variant<int, Trace<false, false>>;
TraceStat stat;
V v{1};
v = &stat;
assert(stat.construct == 1);
assert(stat.copy_construct == 0);
assert(stat.move_construct == 0);
assert(stat.copy_assign == 0);
assert(stat.move_assign == 0);
assert(stat.destroy == 0);
}
{
using V = std::variant<int, Trace<false, true>>;
TraceStat stat;
V v{1};
v = &stat;
assert(stat.construct == 1);
assert(stat.copy_construct == 0);
assert(stat.move_construct == 1);
assert(stat.copy_assign == 0);
assert(stat.move_assign == 0);
assert(stat.destroy == 1);
}

{
using V = std::variant<int, Trace<true, false>>;
TraceStat stat;
V v{1};
v = &stat;
assert(stat.construct == 1);
assert(stat.copy_construct == 0);
assert(stat.move_construct == 0);
assert(stat.copy_assign == 0);
assert(stat.move_assign == 0);
assert(stat.destroy == 0);
}

{
using V = std::variant<int, Trace<true, true>>;
TraceStat stat;
V v{1};
v = &stat;
assert(stat.construct == 1);
assert(stat.copy_construct == 0);
assert(stat.move_construct == 0);
assert(stat.copy_assign == 0);
assert(stat.move_assign == 0);
assert(stat.destroy == 0);
}
}

void test_T_assignment_performs_construction() {
TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_assignment() {
{
using V = std::variant<int, Trace<false, false>>;
TraceStat stat;
V v{&stat};
v = Trace<false, false>::T{};
assert(stat.construct == 1);
assert(stat.copy_construct == 0);
assert(stat.move_construct == 0);
assert(stat.copy_assign == 0);
assert(stat.move_assign == 0);
assert(stat.T_copy_assign == 0);
assert(stat.T_move_assign == 1);
assert(stat.destroy == 0);
}
{
using V = std::variant<int, Trace<false, false>>;
TraceStat stat;
V v{&stat};
Trace<false, false>::T t;
v = t;
assert(stat.construct == 1);
assert(stat.copy_construct == 0);
assert(stat.move_construct == 0);
assert(stat.copy_assign == 0);
assert(stat.move_assign == 0);
assert(stat.T_copy_assign == 1);
assert(stat.T_move_assign == 0);
assert(stat.destroy == 0);
}
}

void test_T_assignment_performs_construction_throw() {
using namespace RuntimeHelpers;
#ifndef TEST_HAS_NO_EXCEPTIONS
{
Expand All @@ -220,7 +348,7 @@ void test_T_assignment_performs_construction() {
#endif // TEST_HAS_NO_EXCEPTIONS
}

void test_T_assignment_performs_assignment() {
void test_T_assignment_performs_assignment_throw() {
using namespace RuntimeHelpers;
#ifndef TEST_HAS_NO_EXCEPTIONS
{
Expand Down Expand Up @@ -262,21 +390,37 @@ void test_T_assignment_performs_assignment() {
#endif // TEST_HAS_NO_EXCEPTIONS
}

void test_T_assignment_vector_bool() {
TEST_CONSTEXPR_CXX20 void test_T_assignment_vector_bool() {
std::vector<bool> vec = {true};
std::variant<bool, int> v;
v = vec[0];
assert(v.index() == 0);
assert(std::get<0>(v) == true);
}

int main(int, char**) {
void non_constexpr_test() {
test_T_assignment_basic_no_constexpr();
test_T_assignment_performs_construction_throw();
test_T_assignment_performs_assignment_throw();
}

TEST_CONSTEXPR_CXX20 bool test() {
test_T_assignment_basic();
test_T_assignment_performs_construction();
test_T_assignment_performs_assignment();
test_T_assignment_noexcept();
test_T_assignment_sfinae();
test_T_assignment_vector_bool();

return true;
}

int main(int, char**) {
test();
non_constexpr_test();

#if TEST_STD_VER >= 20
static_assert(test());
#endif
return 0;
}
Loading
Loading