Skip to content

Commit

Permalink
[libc++] Make constexpr std::variant. Implement P2231R1 (llvm#83335)
Browse files Browse the repository at this point in the history
  • Loading branch information
huixie90 authored May 10, 2024
1 parent 331f22a commit 52271a5
Show file tree
Hide file tree
Showing 16 changed files with 1,322 additions and 836 deletions.
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes/19.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,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

0 comments on commit 52271a5

Please sign in to comment.