blob: bbf5b900f4cdc6894774f6bb0e6d42cb9e19aea4 [file] [log] [blame]
// { dg-options "-std=gnu++23" }
// { dg-do run { target c++23 } }
#include <expected>
#include <type_traits>
#include <testsuite_hooks.h>
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<int> 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<int, int> 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<X, X> 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<void, int> 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<void, X> 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<int, int> e1(1);
std::expected<unsigned, long> 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<void, int> ev4;
std::expected<const void, long> ev5(std::unexpect, 5);
ev4 = ev5;
VERIFY( ! ev4.has_value() );
VERIFY( ev4.error() == 5 );
ev4 = std::expected<volatile void, unsigned>();
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<X, int> e1(0);
e1 = std::unexpected<int>(5);
VERIFY( ! e1.has_value() );
VERIFY( e1.error() == 5 );
VERIFY( check_dtor_count(1) );
e1 = std::unexpected<int>(6);
VERIFY( check_dtor_count(1) );
std::expected<int, X> e2;
std::unexpected<X> 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<int, int> e1(1);
e1.emplace(2);
VERIFY( e1.value() == 2 );
std::expected<void, int> ev2;
ev2.emplace();
VERIFY( ev2.has_value() );
std::expected<X, int> 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<int, X> 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<CopyThrows, MoveThrows> 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<MoveThrows>(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 );
}