// { dg-do run { target c++23 } } #include #include #include int dtor_count; constexpr void reset_dtor_count() { if (!std::is_constant_evaluated()) dtor_count = 0; } constexpr void inc_dtor_count() { if (!std::is_constant_evaluated()) ++dtor_count; } constexpr bool check_dtor_count(int c) { if (std::is_constant_evaluated()) return true; return dtor_count == c; } struct X { constexpr X(int i, int j = 0) noexcept : n(i+j) { } constexpr X(std::initializer_list l, void*) noexcept : n(l.size()) { } constexpr X(const X&) = default; constexpr X(X&& x) noexcept : n(x.n) { x.n = -1; } constexpr X& operator=(const X&) = default; constexpr X& operator=(X&& x) noexcept { n = x.n; x.n = -1; return *this; } constexpr ~X() { inc_dtor_count(); } constexpr bool operator==(const X&) const = default; constexpr bool operator==(int i) const { return n == i; } int n; }; constexpr bool test_copy(bool = true) { reset_dtor_count(); std::expected e1(1), e2(2), e3(std::unexpect, 3); e1 = e1; e1 = e2; // T = T VERIFY( e1.value() == e2.value() ); e1 = e3; // T = E VERIFY( ! e1.has_value() ); VERIFY( e1.error() == e3.error() ); e1 = e3; // E = E VERIFY( ! e1.has_value() ); VERIFY( e1.error() == e3.error() ); e1 = e2; // E = T VERIFY( e1.value() == e2.value() ); e1 = std::move(e1); e1 = std::move(e2); // T = T VERIFY( e1.value() == e2.value() ); e1 = std::move(e3); // T = E VERIFY( ! e1.has_value() ); VERIFY( e1.error() == e3.error() ); e1 = std::move(e3); // E = E VERIFY( ! e1.has_value() ); VERIFY( e1.error() == e3.error() ); e1 = std::move(e2); // E = T VERIFY( e1.value() == e2.value() ); std::expected x1(1), x2(2), x3(std::unexpect, 3); x1 = x1; x1 = x2; // T = T VERIFY( check_dtor_count(0) ); VERIFY( x1.value() == x2.value() ); x1 = x3; // T = E VERIFY( check_dtor_count(1) ); VERIFY( ! x1.has_value() ); x1 = x3; // E = E VERIFY( check_dtor_count(1) ); VERIFY( ! x1.has_value() ); x1 = x2; // E = T VERIFY( check_dtor_count(2) ); VERIFY( x1.value() == x2.value() ); reset_dtor_count(); x1 = std::move(x1); VERIFY( x1.value() == -1 ); x1 = std::move(x2); // T = T VERIFY( check_dtor_count(0) ); VERIFY( x1.value() == 2 ); VERIFY( x2.value() == -1 ); x1 = std::move(x3); // T = E VERIFY( check_dtor_count(1) ); VERIFY( ! x1.has_value() ); VERIFY( x1.error() == 3 ); VERIFY( x3.error() == -1 ); x3.error().n = 33; x1 = std::move(x3); // E = E VERIFY( check_dtor_count(1) ); VERIFY( ! x1.has_value() ); VERIFY( x1.error() == 33 ); VERIFY( x3.error() == -1 ); x2.value().n = 22; x1 = std::move(x2); // E = T VERIFY( check_dtor_count(2) ); VERIFY( x1.value() == 22 ); VERIFY( x2.value() == -1 ); std::expected ev1, ev2, ev3(std::unexpect, 3); ev1 = ev2; // T = T VERIFY( ev1.has_value() ); ev1 = ev3; // T = E VERIFY( ! ev1.has_value() ); VERIFY( ev1.error() == ev3.error() ); ev1 = ev3; // E = E VERIFY( ! ev1.has_value() ); VERIFY( ev1.error() == ev3.error() ); ev1 = ev2; // E = T VERIFY( ev1.has_value() ); reset_dtor_count(); std::expected xv1, xv2, xv3(std::unexpect, 3); xv1 = std::move(xv2); // T = T VERIFY( check_dtor_count(0) ); VERIFY( xv1.has_value() ); xv1 = std::move(xv3); // T = E VERIFY( check_dtor_count(0) ); VERIFY( ! xv1.has_value() ); VERIFY( xv1.error() == 3 ); VERIFY( xv3.error() == -1 ); xv3.error().n = 33; xv1 = std::move(xv3); // E = E VERIFY( check_dtor_count(0) ); VERIFY( xv1.error() == 33 ); VERIFY( xv3.error() == -1 ); xv1 = std::move(xv2); // E = T VERIFY( check_dtor_count(1) ); VERIFY( xv1.has_value() ); return true; } constexpr bool test_converting(bool = true) { std::expected e1(1); std::expected e2(2U), e3(std::unexpect, 3L); e1 = e2; VERIFY( e1.value() == e2.value() ); e1 = e3; VERIFY( ! e1.has_value() ); VERIFY( e1.error() == e3.error() ); e1 = e2; VERIFY( e1.value() == e2.value() ); e1 = std::move(e3); VERIFY( ! e1.has_value() ); VERIFY( e1.error() == e3.error() ); e1 = std::move(e2); VERIFY( e1.value() == e2.value() ); std::expected ev4; std::expected ev5(std::unexpect, 5); ev4 = ev5; VERIFY( ! ev4.has_value() ); VERIFY( ev4.error() == 5 ); ev4 = std::expected(); VERIFY( ev4.has_value() ); ev4 = std::move(ev5); VERIFY( ! ev4.has_value() ); VERIFY( ev4.error() == 5 ); return true; } constexpr bool test_unexpected(bool = true) { reset_dtor_count(); std::expected e1(0); e1 = std::unexpected(5); VERIFY( ! e1.has_value() ); VERIFY( e1.error() == 5 ); VERIFY( check_dtor_count(1) ); e1 = std::unexpected(6); VERIFY( check_dtor_count(1) ); std::expected e2; std::unexpected x(std::in_place, 1, 2); e2 = x; VERIFY( check_dtor_count(1) ); e2 = 1; VERIFY( e2.value() == 1 ); VERIFY( check_dtor_count(2) ); return true; } constexpr bool test_emplace(bool = true) { reset_dtor_count(); std::expected e1(1); e1.emplace(2); VERIFY( e1.value() == 2 ); std::expected ev2; ev2.emplace(); VERIFY( ev2.has_value() ); std::expected e3(std::in_place, 0, 0); e3.emplace({1,2,3}, nullptr); VERIFY( e3.value() == 3 ); VERIFY( check_dtor_count(1) ); e3.emplace(2, 2); VERIFY( e3.value() == 4 ); VERIFY( check_dtor_count(2) ); std::expected ev4(std::unexpect, 4); ev4.emplace(5); VERIFY( ev4.value() == 5 ); VERIFY( check_dtor_count(3) ); ev4.emplace(6); VERIFY( ev4.value() == 6 ); VERIFY( check_dtor_count(3) ); return true; } void test_exception_safety() { struct CopyThrows { CopyThrows(int i) noexcept : x(i) { } CopyThrows(const CopyThrows&) { throw 1; } CopyThrows(CopyThrows&&) = default; CopyThrows& operator=(const CopyThrows&) = default; CopyThrows& operator=(CopyThrows&&) = default; int x; bool operator==(int i) const { return x == i; } }; struct MoveThrows { MoveThrows(int i) noexcept : x(i) { } MoveThrows(const MoveThrows&) = default; MoveThrows(MoveThrows&&) { throw 1L; } MoveThrows& operator=(const MoveThrows&) = default; MoveThrows& operator=(MoveThrows&&) = default; int x; bool operator==(int i) const { return x == i; } }; std::expected c(std::unexpect, 1); // operator=(U&&) try { CopyThrows x(2); c = x; VERIFY( false ); } catch (int) { VERIFY( ! c.has_value() ); VERIFY( c.error() == 1 ); } c = CopyThrows(2); try { c = std::unexpected(3); VERIFY( false ); } catch (long) { VERIFY( c.value() == 2 ); } } int main(int argc, char**) { bool non_constant = argc == 1; // force non-constant evaluation static_assert( test_copy() ); test_copy(non_constant); static_assert( test_converting() ); test_converting(non_constant); static_assert( test_unexpected() ); test_unexpected(non_constant); static_assert( test_emplace() ); test_emplace(non_constant); test_exception_safety(); // Ensure the non-constexpr tests actually ran: VERIFY( dtor_count != 0 ); }