Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class U, class G>
// constexpr explicit(see below) expected(const expected<U, G>&);
//
// Let:
// - UF be const U&
// - GF be const G&
//
// Constraints:
// - is_constructible_v<T, UF> is true; and
// - is_constructible_v<E, GF> is true; and
// - is_constructible_v<T, expected<U, G>&> is false; and
// - is_constructible_v<T, expected<U, G>> is false; and
// - is_constructible_v<T, const expected<U, G>&> is false; and
// - is_constructible_v<T, const expected<U, G>> is false; and
// - is_convertible_v<expected<U, G>&, T> is false; and
// - is_convertible_v<expected<U, G>&&, T> is false; and
// - is_convertible_v<const expected<U, G>&, T> is false; and
// - is_convertible_v<const expected<U, G>&&, T> is false; and
// - is_constructible_v<unexpected<E>, expected<U, G>&> is false; and
// - is_constructible_v<unexpected<E>, expected<U, G>> is false; and
// - is_constructible_v<unexpected<E>, const expected<U, G>&> is false; and
// - is_constructible_v<unexpected<E>, const expected<U, G>> is false.
//
// Effects: If rhs.has_value(), direct-non-list-initializes val with std::forward<UF>(*rhs). Otherwise, direct-non-list-initializes unex with std::forward<GF>(rhs.error()).
//
// Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true.
//
// Throws: Any exception thrown by the initialization of val or unex.
//
// Remarks: The expression inside explicit is equivalent to !is_convertible_v<UF, T> || !is_convertible_v<GF, E>.

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

// Test Constraints:
template <class T1, class Err1, class T2, class Err2>
concept canCstrFromExpected = std::is_constructible_v<std::expected<T1, Err1>, const std::expected<T2, Err2>&>;

struct CtorFromInt {
CtorFromInt(int);
};

static_assert(canCstrFromExpected<CtorFromInt, int, int, int>);

struct NoCtorFromInt {};

// !is_constructible_v<T, UF>
static_assert(!canCstrFromExpected<NoCtorFromInt, int, int, int>);

// !is_constructible_v<E, GF>
static_assert(!canCstrFromExpected<int, NoCtorFromInt, int, int>);

template <class T>
struct CtorFrom {
explicit CtorFrom(int)
requires(!std::same_as<T, int>);
explicit CtorFrom(T);
explicit CtorFrom(auto&&) = delete;
};

// is_constructible_v<T, expected<U, G>&>
static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int>&>, int, int, int>);

// is_constructible_v<T, expected<U, G>>
static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int>&&>, int, int, int>);

// is_constructible_v<T, expected<U, G>&>
// note that this is true because it is covered by the other overload
// template<class U = T> constexpr explicit(see below) expected(U&& v);
// The fact that it is not ambiguous proves that the overload under testing is removed
static_assert(canCstrFromExpected<CtorFrom<std::expected<int, int> const&>, int, int, int>);

// is_constructible_v<T, expected<U, G>>
static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int> const&&>, int, int, int>);

template <class T>
struct ConvertFrom {
ConvertFrom(int)
requires(!std::same_as<T, int>);
ConvertFrom(T);
ConvertFrom(auto&&) = delete;
};

// is_convertible_v<expected<U, G>&, T>
static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int>&>, int, int, int>);

// is_convertible_v<expected<U, G>&&, T>
static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int>&&>, int, int, int>);

// is_convertible_v<const expected<U, G>&, T>
// note that this is true because it is covered by the other overload
// template<class U = T> constexpr explicit(see below) expected(U&& v);
// The fact that it is not ambiguous proves that the overload under testing is removed
static_assert(canCstrFromExpected<ConvertFrom<std::expected<int, int> const&>, int, int, int>);

// is_convertible_v<const expected<U, G>&&, T>
static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int> const&&>, int, int, int>);

// Note for below 4 tests, because their E is constructible from cvref of std::expected<int, int>,
// unexpected<E> will be constructible from cvref of std::expected<int, int>
// is_constructible_v<unexpected<E>, expected<U, G>&>
static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&>, int, int>);

// is_constructible_v<unexpected<E>, expected<U, G>>
static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&&>, int, int>);

// is_constructible_v<unexpected<E>, const expected<U, G>&> is false
static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&>, int, int>);

// is_constructible_v<unexpected<E>, const expected<U, G>>
static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&&>, int, int>);

// test explicit
static_assert(std::is_convertible_v<const std::expected<int, int>&, std::expected<short, long>>);

// !is_convertible_v<UF, T>
static_assert(std::is_constructible_v<std::expected<CtorFrom<int>, int>, const std::expected<int, int>&>);
static_assert(!std::is_convertible_v<const std::expected<int, int>&, std::expected<CtorFrom<int>, int>>);

// !is_convertible_v<GF, E>.
static_assert(std::is_constructible_v<std::expected<int, CtorFrom<int>>, const std::expected<int, int>&>);
static_assert(!std::is_convertible_v<const std::expected<int, int>&, std::expected<int, CtorFrom<int>>>);

struct Data {
int i;
constexpr Data(int ii) : i(ii) {}
};

constexpr bool test() {
// convert the value
{
const std::expected<int, int> e1(5);
std::expected<Data, int> e2 = e1;
assert(e2.has_value());
assert(e2.value().i == 5);
assert(e1.has_value());
assert(e1.value() == 5);
}

// convert the error
{
const std::expected<int, int> e1(std::unexpect, 5);
std::expected<int, Data> e2 = e1;
assert(!e2.has_value());
assert(e2.error().i == 5);
assert(!e1.has_value());
assert(e1.error() == 5);
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct ThrowingInt {
ThrowingInt(int) { throw Except{}; }
};

// throw on converting value
{
const std::expected<int, int> e1;
try {
[[maybe_unused]] std::expected<ThrowingInt, int> e2 = e1;
assert(false);
} catch (Except) {
}
}

// throw on converting error
{
const std::expected<int, int> e1(std::unexpect);
try {
[[maybe_unused]] std::expected<int, ThrowingInt> e2 = e1;
assert(false);
} catch (Except) {
}
}

#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class U, class G>
// constexpr explicit(see below) expected(expected<U, G>&&);
//
// Let:
// - UF be const U
// - GF be const G
//
// Constraints:
// - is_constructible_v<T, UF> is true; and
// - is_constructible_v<E, GF> is true; and
// - is_constructible_v<T, expected<U, G>&> is false; and
// - is_constructible_v<T, expected<U, G>> is false; and
// - is_constructible_v<T, const expected<U, G>&> is false; and
// - is_constructible_v<T, const expected<U, G>> is false; and
// - is_convertible_v<expected<U, G>&, T> is false; and
// - is_convertible_v<expected<U, G>&&, T> is false; and
// - is_convertible_v<const expected<U, G>&, T> is false; and
// - is_convertible_v<const expected<U, G>&&, T> is false; and
// - is_constructible_v<unexpected<E>, expected<U, G>&> is false; and
// - is_constructible_v<unexpected<E>, expected<U, G>> is false; and
// - is_constructible_v<unexpected<E>, const expected<U, G>&> is false; and
// - is_constructible_v<unexpected<E>, const expected<U, G>> is false.
//
// Effects: If rhs.has_value(), direct-non-list-initializes val with std::forward<UF>(*rhs). Otherwise, direct-non-list-initializes unex with std::forward<GF>(rhs.error()).
//
// Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true.
//
// Throws: Any exception thrown by the initialization of val or unex.
//
// Remarks: The expression inside explicit is equivalent to !is_convertible_v<UF, T> || !is_convertible_v<GF, E>.

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "MoveOnly.h"
#include "test_macros.h"

// Test Constraints:
template <class T1, class Err1, class T2, class Err2>
concept canCstrFromExpected = std::is_constructible_v<std::expected<T1, Err1>, std::expected<T2, Err2>&&>;

struct CtorFromInt {
CtorFromInt(int);
};

static_assert(canCstrFromExpected<CtorFromInt, int, int, int>);

struct NoCtorFromInt {};

// !is_constructible_v<T, UF>
static_assert(!canCstrFromExpected<NoCtorFromInt, int, int, int>);

// !is_constructible_v<E, GF>
static_assert(!canCstrFromExpected<int, NoCtorFromInt, int, int>);

template <class T>
struct CtorFrom {
explicit CtorFrom(int)
requires(!std::same_as<T, int>);
explicit CtorFrom(T);
explicit CtorFrom(auto&&) = delete;
};

// is_constructible_v<T, expected<U, G>&>
static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int>&>, int, int, int>);

// is_constructible_v<T, expected<U, G>>
// note that this is true because it is covered by the other overload
// template<class U = T> constexpr explicit(see below) expected(U&& v);
// The fact that it is not ambiguous proves that the overload under testing is removed
static_assert(canCstrFromExpected<CtorFrom<std::expected<int, int>&&>, int, int, int>);

// is_constructible_v<T, expected<U, G>&>
static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int> const&>, int, int, int>);

// is_constructible_v<T, expected<U, G>>
static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int> const&&>, int, int, int>);

template <class T>
struct ConvertFrom {
ConvertFrom(int)
requires(!std::same_as<T, int>);
ConvertFrom(T);
ConvertFrom(auto&&) = delete;
};

// is_convertible_v<expected<U, G>&, T>
static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int>&>, int, int, int>);

// is_convertible_v<expected<U, G>&&, T>
// note that this is true because it is covered by the other overload
// template<class U = T> constexpr explicit(see below) expected(U&& v);
// The fact that it is not ambiguous proves that the overload under testing is removed
static_assert(canCstrFromExpected<ConvertFrom<std::expected<int, int>&&>, int, int, int>);

// is_convertible_v<const expected<U, G>&, T>
static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int> const&>, int, int, int>);

// is_convertible_v<const expected<U, G>&&, T>
static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int> const&&>, int, int, int>);

// is_constructible_v<unexpected<E>, expected<U, G>&>
static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&>, int, int>);

// is_constructible_v<unexpected<E>, expected<U, G>>
static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&&>, int, int>);

// is_constructible_v<unexpected<E>, const expected<U, G>&> is false
static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&>, int, int>);

// is_constructible_v<unexpected<E>, const expected<U, G>>
static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&&>, int, int>);

// test explicit
static_assert(std::is_convertible_v<std::expected<int, int>&&, std::expected<short, long>>);

// !is_convertible_v<UF, T>
static_assert(std::is_constructible_v<std::expected<CtorFrom<int>, int>, std::expected<int, int>&&>);
static_assert(!std::is_convertible_v<std::expected<int, int>&&, std::expected<CtorFrom<int>, int>>);

// !is_convertible_v<GF, E>.
static_assert(std::is_constructible_v<std::expected<int, CtorFrom<int>>, std::expected<int, int>&&>);
static_assert(!std::is_convertible_v<std::expected<int, int>&&, std::expected<int, CtorFrom<int>>>);

struct Data {
MoveOnly data;
constexpr Data(MoveOnly&& m) : data(std::move(m)) {}
};

constexpr bool test() {
// convert the value
{
std::expected<MoveOnly, int> e1(5);
std::expected<Data, int> e2 = std::move(e1);
assert(e2.has_value());
assert(e2.value().data.get() == 5);
assert(e1.has_value());
assert(e1.value().get() == 0);
}

// convert the error
{
std::expected<int, MoveOnly> e1(std::unexpect, 5);
std::expected<int, Data> e2 = std::move(e1);
assert(!e2.has_value());
assert(e2.error().data.get() == 5);
assert(!e1.has_value());
assert(e1.error().get() == 0);
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct ThrowingInt {
ThrowingInt(int) { throw Except{}; }
};

// throw on converting value
{
const std::expected<int, int> e1;
try {
[[maybe_unused]] std::expected<ThrowingInt, int> e2 = e1;
assert(false);
} catch (Except) {
}
}

// throw on converting error
{
const std::expected<int, int> e1(std::unexpect);
try {
[[maybe_unused]] std::expected<int, ThrowingInt> e2 = e1;
assert(false);
} catch (Except) {
}
}

#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr expected(const expected& rhs);
//
// Effects: If rhs.has_value() is true, direct-non-list-initializes val with *rhs.
// Otherwise, direct-non-list-initializes unex with rhs.error().
//
// Postconditions: rhs.has_value() == this->has_value().
//
// Throws: Any exception thrown by the initialization of val or unex.
//
// Remarks: This constructor is defined as deleted unless
// - is_copy_constructible_v<T> is true and
// - is_copy_constructible_v<E> is true.
//
// This constructor is trivial if
// - is_trivially_copy_constructible_v<T> is true and
// - is_trivially_copy_constructible_v<E> is true.

#include <cassert>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

struct NonCopyable {
NonCopyable(const NonCopyable&) = delete;
};

struct CopyableNonTrivial {
int i;
constexpr CopyableNonTrivial(int ii) : i(ii) {}
constexpr CopyableNonTrivial(const CopyableNonTrivial& o) { i = o.i; }
friend constexpr bool operator==(const CopyableNonTrivial&, const CopyableNonTrivial&) = default;
};

// Test: This constructor is defined as deleted unless
// - is_copy_constructible_v<T> is true and
// - is_copy_constructible_v<E> is true.
static_assert(std::is_copy_constructible_v<std::expected<int, int>>);
static_assert(std::is_copy_constructible_v<std::expected<CopyableNonTrivial, int>>);
static_assert(std::is_copy_constructible_v<std::expected<int, CopyableNonTrivial>>);
static_assert(std::is_copy_constructible_v<std::expected<CopyableNonTrivial, CopyableNonTrivial>>);
static_assert(!std::is_copy_constructible_v<std::expected<NonCopyable, int>>);
static_assert(!std::is_copy_constructible_v<std::expected<int, NonCopyable>>);
static_assert(!std::is_copy_constructible_v<std::expected<NonCopyable, NonCopyable>>);

// Test: This constructor is trivial if
// - is_trivially_copy_constructible_v<T> is true and
// - is_trivially_copy_constructible_v<E> is true.
#if __cpp_concepts >= 202002
static_assert(std::is_trivially_copy_constructible_v<std::expected<int, int>>);
#endif
static_assert(!std::is_trivially_copy_constructible_v<std::expected<CopyableNonTrivial, int>>);
static_assert(!std::is_trivially_copy_constructible_v<std::expected<int, CopyableNonTrivial>>);
static_assert(!std::is_trivially_copy_constructible_v<std::expected<CopyableNonTrivial, CopyableNonTrivial>>);

constexpr bool test() {
// copy the value non-trivial
{
const std::expected<CopyableNonTrivial, int> e1(5);
auto e2 = e1;
assert(e2.has_value());
assert(e2.value().i == 5);
}

// copy the error non-trivial
{
const std::expected<int, CopyableNonTrivial> e1(std::unexpect, 5);
auto e2 = e1;
assert(!e2.has_value());
assert(e2.error().i == 5);
}

// copy the value trivial
{
const std::expected<int, int> e1(5);
auto e2 = e1;
assert(e2.has_value());
assert(e2.value() == 5);
}

// copy the error trivial
{
const std::expected<int, int> e1(std::unexpect, 5);
auto e2 = e1;
assert(!e2.has_value());
assert(e2.error() == 5);
}
return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing() = default;
Throwing(const Throwing&) { throw Except{}; }
};

// throw on copying value
{
const std::expected<Throwing, int> e1;
try {
[[maybe_unused]] auto e2 = e1;
assert(false);
} catch (Except) {
}
}

// throw on copying error
{
const std::expected<int, Throwing> e1(std::unexpect);
try {
[[maybe_unused]] auto e2 = e1;
assert(false);
} catch (Except) {
}
}

#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

//
// constexpr expected();

// Constraints: is_default_constructible_v<T> is true.
//
// Effects: Value-initializes val.
// Postconditions: has_value() is true.
//
// Throws: Any exception thrown by the initialization of val.

#include <cassert>
#include <expected>
#include <type_traits>

#include "test_macros.h"

struct NoDedefaultCtor {
NoDedefaultCtor() = delete;
};

// Test constraints
static_assert(std::is_default_constructible_v<std::expected<int, int>>);
static_assert(!std::is_default_constructible_v<std::expected<NoDedefaultCtor, int>>);

struct MyInt {
int i;
friend constexpr bool operator==(const MyInt&, const MyInt&) = default;
};

template <class T, class E>
constexpr void testDefaultCtor() {
std::expected<T, E> e;
assert(e.has_value());
assert(e.value() == T());
}

template <class T>
constexpr void testTypes() {
testDefaultCtor<T, int>();
testDefaultCtor<T, NoDedefaultCtor>();
}

constexpr bool test() {
testTypes<int>();
testTypes<MyInt>();
return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing() { throw Except{}; };
};

try {
std::expected<Throwing, int> u;
assert(false);
} catch (Except) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class... Args>
// constexpr explicit expected(in_place_t, Args&&... args);
//
// Constraints: is_constructible_v<T, Args...> is true.
//
// Effects: Direct-non-list-initializes val with std::forward<Args>(args)....
//
// Postconditions: has_value() is true.
//
// Throws: Any exception thrown by the initialization of val.

#include <cassert>
#include <expected>
#include <type_traits>
#include <tuple>
#include <utility>

#include "MoveOnly.h"
#include "test_macros.h"

// Test Constraints:
static_assert(std::is_constructible_v<std::expected<int, int>, std::in_place_t>);
static_assert(std::is_constructible_v<std::expected<int, int>, std::in_place_t, int>);

// !is_constructible_v<T, Args...>
struct foo {};
static_assert(!std::is_constructible_v<std::expected<foo, int>, std::in_place_t, int>);

// test explicit
template <class T>
void conversion_test(T);

template <class T, class... Args>
concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
static_assert(ImplicitlyConstructible<int, int>);

static_assert(!ImplicitlyConstructible<std::expected<int, int>, std::in_place_t>);
static_assert(!ImplicitlyConstructible<std::expected<int, int>, std::in_place_t, int>);

struct CopyOnly {
int i;
constexpr CopyOnly(int ii) : i(ii) {}
CopyOnly(const CopyOnly&) = default;
CopyOnly(CopyOnly&&) = delete;
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
};

template <class T>
constexpr void testInt() {
std::expected<T, int> e(std::in_place, 5);
assert(e.has_value());
assert(e.value() == 5);
}

template <class T>
constexpr void testLValue() {
T t(5);
std::expected<T, int> e(std::in_place, t);
assert(e.has_value());
assert(e.value() == 5);
}

template <class T>
constexpr void testRValue() {
std::expected<T, int> e(std::in_place, T(5));
assert(e.has_value());
assert(e.value() == 5);
}

constexpr bool test() {
testInt<int>();
testInt<CopyOnly>();
testInt<MoveOnly>();
testLValue<int>();
testLValue<CopyOnly>();
testRValue<int>();
testRValue<MoveOnly>();

// no arg
{
std::expected<int, int> e(std::in_place);
assert(e.has_value());
assert(e.value() == 0);
}

// one arg
{
std::expected<int, int> e(std::in_place, 5);
assert(e.has_value());
assert(e.value() == 5);
}

// multi args
{
std::expected<std::tuple<int, short, MoveOnly>, int> e(std::in_place, 1, 2, MoveOnly(3));
assert(e.has_value());
assert((e.value() == std::tuple<int, short, MoveOnly>(1, 2, MoveOnly(3))));
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing(int) { throw Except{}; };
};

try {
std::expected<Throwing, int> u(std::in_place, 5);
assert(false);
} catch (Except) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// Older Clangs do not support the C++20 feature to constrain destructors
// XFAIL: clang-14, apple-clang-14

// template<class U, class... Args>
// constexpr explicit expected(in_place_t, initializer_list<U> il, Args&&... args);
//
// Constraints: is_constructible_v<T, initializer_list<U>&, Args...> is true.
//
// Effects: Direct-non-list-initializes val with il, std::forward<Args>(args)....
//
// Postconditions: has_value() is true.
//
// Throws: Any exception thrown by the initialization of val.

#include <algorithm>
#include <cassert>
#include <expected>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

#include "MoveOnly.h"
#include "test_macros.h"

// Test Constraints:
static_assert(
std::is_constructible_v<std::expected<std::vector<int>, int>, std::in_place_t, std::initializer_list<int>>);

// !is_constructible_v<T, initializer_list<U>&, Args...>
static_assert(!std::is_constructible_v<std::expected<int, int>, std::in_place_t, std::initializer_list<int>>);

// test explicit
template <class T>
void conversion_test(T);

template <class T, class... Args>
concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
static_assert(ImplicitlyConstructible<int, int>);

static_assert(
!ImplicitlyConstructible<std::expected<std::vector<int>, int>, std::in_place_t, std::initializer_list<int>>);

template <class... Ts>
struct Data {
std::vector<int> vec_;
std::tuple<Ts...> tuple_;

template <class... Us>
requires std::is_constructible_v<std::tuple<Ts...>, Us&&...>
constexpr Data(std::initializer_list<int> il, Us&&... us) : vec_(il), tuple_(std::forward<Us>(us)...) {}
};

constexpr bool test() {
// no arg
{
std::expected<Data<>, int> e(std::in_place, {1, 2, 3});
assert(e.has_value());
auto expectedList = {1, 2, 3};
assert(std::ranges::equal(e.value().vec_, expectedList));
}

// one arg
{
std::expected<Data<MoveOnly>, int> e(std::in_place, {4, 5, 6}, MoveOnly(5));
assert(e.has_value());
auto expectedList = {4, 5, 6};
assert((std::ranges::equal(e.value().vec_, expectedList)));
assert(std::get<0>(e.value().tuple_) == 5);
}

// multi args
{
int i = 5;
int j = 6;
MoveOnly m(7);
std::expected<Data<int&, int&&, MoveOnly>, int> e(std::in_place, {1, 2}, i, std::move(j), std::move(m));
assert(e.has_value());
auto expectedList = {1, 2};
assert((std::ranges::equal(e.value().vec_, expectedList)));
assert(&std::get<0>(e.value().tuple_) == &i);
assert(&std::get<1>(e.value().tuple_) == &j);
assert(std::get<2>(e.value().tuple_) == 7);
assert(m.get() == 0);
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing(std::initializer_list<int>, int) { throw Except{}; };
};

try {
std::expected<Throwing, int> u(std::in_place, {1, 2}, 5);
assert(false);
} catch (Except) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr expected(expected&& rhs) noexcept(see below);
//
// Constraints:
// - is_move_constructible_v<T> is true and
// - is_move_constructible_v<E> is true.
//
// Effects: If rhs.has_value() is true, direct-non-list-initializes val with std::move(*rhs).
// Otherwise, direct-non-list-initializes unex with std::move(rhs.error()).
//
// Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true.
//
// Throws: Any exception thrown by the initialization of val or unex.
//
// Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>.
//
// This constructor is trivial if
// - is_trivially_move_constructible_v<T> is true and
// - is_trivially_move_constructible_v<E> is true.

#include <cassert>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

struct NonMovable {
NonMovable(NonMovable&&) = delete;
};

struct MovableNonTrivial {
int i;
constexpr MovableNonTrivial(int ii) : i(ii) {}
constexpr MovableNonTrivial(MovableNonTrivial&& o) : i(o.i) { o.i = 0; }
friend constexpr bool operator==(const MovableNonTrivial&, const MovableNonTrivial&) = default;
};

struct MoveMayThrow {
MoveMayThrow(MoveMayThrow&&) {}
};

// Test Constraints:
// - is_move_constructible_v<T> is true and
// - is_move_constructible_v<E> is true.
static_assert(std::is_move_constructible_v<std::expected<int, int>>);
static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, int>>);
static_assert(std::is_move_constructible_v<std::expected<int, MovableNonTrivial>>);
static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>);
static_assert(!std::is_move_constructible_v<std::expected<NonMovable, int>>);
static_assert(!std::is_move_constructible_v<std::expected<int, NonMovable>>);
static_assert(!std::is_move_constructible_v<std::expected<NonMovable, NonMovable>>);

// Test: This constructor is trivial if
// - is_trivially_move_constructible_v<T> is true and
// - is_trivially_move_constructible_v<E> is true.
#if __cpp_concepts >= 202002
static_assert(std::is_trivially_move_constructible_v<std::expected<int, int>>);
#endif
static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, int>>);
static_assert(!std::is_trivially_move_constructible_v<std::expected<int, MovableNonTrivial>>);
static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>);

// Test: The exception specification is equivalent to
// is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>.
static_assert(std::is_nothrow_move_constructible_v<std::expected<int, int>>);
static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, int>>);
static_assert(!std::is_nothrow_move_constructible_v<std::expected<int, MoveMayThrow>>);
static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, MoveMayThrow>>);

constexpr bool test() {
// move the value non-trivial
{
std::expected<MovableNonTrivial, int> e1(5);
auto e2 = std::move(e1);
assert(e2.has_value());
assert(e2.value().i == 5);
assert(e1.has_value());
assert(e1.value().i == 0);
}

// move the error non-trivial
{
std::expected<int, MovableNonTrivial> e1(std::unexpect, 5);
auto e2 = std::move(e1);
assert(!e2.has_value());
assert(e2.error().i == 5);
assert(!e1.has_value());
assert(e1.error().i == 0);
}

// move the value trivial
{
std::expected<int, int> e1(5);
auto e2 = std::move(e1);
assert(e2.has_value());
assert(e2.value() == 5);
assert(e1.has_value());
}

// move the error trivial
{
std::expected<int, int> e1(std::unexpect, 5);
auto e2 = std::move(e1);
assert(!e2.has_value());
assert(e2.error() == 5);
assert(!e1.has_value());
}
return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing() = default;
Throwing(Throwing&&) { throw Except{}; }
};

// throw on moving value
{
std::expected<Throwing, int> e1;
try {
[[maybe_unused]] auto e2 = std::move(e1);
assert(false);
} catch (Except) {
}
}

// throw on moving error
{
std::expected<int, Throwing> e1(std::unexpect);
try {
[[maybe_unused]] auto e2 = std::move(e1);
assert(false);
} catch (Except) {
}
}

#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class U = T>
// constexpr explicit(!is_convertible_v<U, T>) expected(U&& v);
//
// Constraints:
// - is_same_v<remove_cvref_t<U>, in_place_t> is false; and
// - is_same_v<expected, remove_cvref_t<U>> is false; and
// - remove_cvref_t<U> is not a specialization of unexpected; and
// - is_constructible_v<T, U> is true.
//
// Effects: Direct-non-list-initializes val with std::forward<U>(v).
//
// Postconditions: has_value() is true.
//
// Throws: Any exception thrown by the initialization of val.

#include <cassert>
#include <expected>
#include <type_traits>
#include <utility>

#include "MoveOnly.h"
#include "test_macros.h"

// Test Constraints:
static_assert(std::is_constructible_v<std::expected<int, int>, int>);

// is_same_v<remove_cvref_t<U>, in_place_t>
struct FromJustInplace {
FromJustInplace(std::in_place_t);
};
static_assert(!std::is_constructible_v<std::expected<FromJustInplace, int>, std::in_place_t>);
static_assert(!std::is_constructible_v<std::expected<FromJustInplace, int>, std::in_place_t const&>);

// is_same_v<expected, remove_cvref_t<U>>
// Note that result is true because it is covered by the constructors that take expected
static_assert(std::is_constructible_v<std::expected<int, int>, std::expected<int, int>&>);

// remove_cvref_t<U> is a specialization of unexpected
// Note that result is true because it is covered by the constructors that take unexpected
static_assert(std::is_constructible_v<std::expected<int, int>, std::unexpected<int>&>);

// !is_constructible_v<T, U>
struct foo {};
static_assert(!std::is_constructible_v<std::expected<int, int>, foo>);

// test explicit(!is_convertible_v<U, T>)
struct NotConvertible {
explicit NotConvertible(int);
};
static_assert(std::is_convertible_v<int, std::expected<int, int>>);
static_assert(!std::is_convertible_v<int, std::expected<NotConvertible, int>>);

struct CopyOnly {
int i;
constexpr CopyOnly(int ii) : i(ii) {}
CopyOnly(const CopyOnly&) = default;
CopyOnly(CopyOnly&&) = delete;
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
};

template <class T>
constexpr void testInt() {
std::expected<T, int> e(5);
assert(e.has_value());
assert(e.value() == 5);
}

template <class T>
constexpr void testLValue() {
T t(5);
std::expected<T, int> e(t);
assert(e.has_value());
assert(e.value() == 5);
}

template <class T>
constexpr void testRValue() {
std::expected<T, int> e(T(5));
assert(e.has_value());
assert(e.value() == 5);
}

constexpr bool test() {
testInt<int>();
testInt<CopyOnly>();
testInt<MoveOnly>();
testLValue<int>();
testLValue<CopyOnly>();
testRValue<int>();
testRValue<MoveOnly>();

// Test default template argument.
// Without it, the template parameter cannot be deduced from an initializer list
{
struct Bar {
int i;
int j;
constexpr Bar(int ii, int jj) : i(ii), j(jj) {}
};

std::expected<Bar, int> e({5, 6});
assert(e.value().i == 5);
assert(e.value().j == 6);
}

// this is a confusing example, but the behaviour
// is exactly what is specified in the spec
// see https://cplusplus.github.io/LWG/issue3836
{
struct BaseError {};
struct DerivedError : BaseError {};

std::expected<bool, DerivedError> e1(false);
std::expected<bool, BaseError> e2(e1);
assert(e2.has_value());
assert(e2.value()); // yes, e2 holds "true"
}
return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing(int) { throw Except{}; };
};

try {
std::expected<Throwing, int> u(5);
assert(false);
} catch (Except) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class... Args>
// constexpr explicit expected(unexpect_t, Args&&... args);
//
// Constraints: is_constructible_v<E, Args...> is true.
//
// Effects: Direct-non-list-initializes unex with std::forward<Args>(args)....
//
// Postconditions: has_value() is false.
//
// Throws: Any exception thrown by the initialization of unex.

#include <cassert>
#include <expected>
#include <tuple>
#include <type_traits>
#include <utility>

#include "MoveOnly.h"
#include "test_macros.h"

// Test Constraints:
static_assert(std::is_constructible_v<std::expected<int, int>, std::unexpect_t>);
static_assert(std::is_constructible_v<std::expected<int, int>, std::unexpect_t, int>);

// !is_constructible_v<T, Args...>
struct foo {};
static_assert(!std::is_constructible_v<std::expected<int, foo>, std::unexpect_t, int>);

// test explicit
template <class T>
void conversion_test(T);

template <class T, class... Args>
concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
static_assert(ImplicitlyConstructible<int, int>);

static_assert(!ImplicitlyConstructible<std::expected<int, int>, std::unexpect_t>);
static_assert(!ImplicitlyConstructible<std::expected<int, int>, std::unexpect_t, int>);

struct CopyOnly {
int i;
constexpr CopyOnly(int ii) : i(ii) {}
CopyOnly(const CopyOnly&) = default;
CopyOnly(CopyOnly&&) = delete;
friend constexpr bool operator==(const CopyOnly& mi, int ii) { return mi.i == ii; }
};

template <class T>
constexpr void testInt() {
std::expected<int, T> e(std::unexpect, 5);
assert(!e.has_value());
assert(e.error() == 5);
}

template <class T>
constexpr void testLValue() {
T t(5);
std::expected<int, T> e(std::unexpect, t);
assert(!e.has_value());
assert(e.error() == 5);
}

template <class T>
constexpr void testRValue() {
std::expected<int, T> e(std::unexpect, T(5));
assert(!e.has_value());
assert(e.error() == 5);
}

constexpr bool test() {
testInt<int>();
testInt<CopyOnly>();
testInt<MoveOnly>();
testLValue<int>();
testLValue<CopyOnly>();
testRValue<int>();
testRValue<MoveOnly>();

// no arg
{
std::expected<int, int> e(std::unexpect);
assert(!e.has_value());
assert(e.error() == 0);
}

// one arg
{
std::expected<int, int> e(std::unexpect, 5);
assert(!e.has_value());
assert(e.error() == 5);
}

// multi args
{
std::expected<int, std::tuple<int, short, MoveOnly>> e(std::unexpect, 1, 2, MoveOnly(3));
assert(!e.has_value());
assert((e.error() == std::tuple<int, short, MoveOnly>(1, 2, MoveOnly(3))));
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing(int) { throw Except{}; };
};

try {
std::expected<int, Throwing> u(std::unexpect, 5);
assert(false);
} catch (Except) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// Older Clangs do not support the C++20 feature to constrain destructors
// XFAIL: clang-14, apple-clang-14

// template<class U, class... Args>
// constexpr explicit expected(unexpect_t, initializer_list<U> il, Args&&... args);
//
// Constraints: is_constructible_v<E, initializer_list<U>&, Args...> is true.
//
// Effects: Direct-non-list-initializes unex with il, std::forward<Args>(args)....
//
// Postconditions: has_value() is false.
//
// Throws: Any exception thrown by the initialization of unex.

#include <algorithm>
#include <cassert>
#include <expected>
#include <tuple>
#include <type_traits>
#include <utility>
#include <vector>

#include "MoveOnly.h"
#include "test_macros.h"

// Test Constraints:
static_assert(
std::is_constructible_v<std::expected<int, std::vector<int>>, std::unexpect_t, std::initializer_list<int>>);

// !is_constructible_v<T, initializer_list<U>&, Args...>
static_assert(!std::is_constructible_v<std::expected<int, int>, std::unexpect_t, std::initializer_list<int>>);

// test explicit
template <class T>
void conversion_test(T);

template <class T, class... Args>
concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
static_assert(ImplicitlyConstructible<int, int>);

static_assert(
!ImplicitlyConstructible<std::expected<int, std::vector<int>>, std::unexpect_t, std::initializer_list<int>>);

template <class... Ts>
struct Data {
std::vector<int> vec_;
std::tuple<Ts...> tuple_;

template <class... Us>
requires std::is_constructible_v<std::tuple<Ts...>, Us&&...>
constexpr Data(std::initializer_list<int> il, Us&&... us) : vec_(il), tuple_(std::forward<Us>(us)...) {}
};

constexpr bool test() {
// no arg
{
std::expected<int, Data<>> e(std::unexpect, {1, 2, 3});
assert(!e.has_value());
auto expectedList = {1, 2, 3};
assert(std::ranges::equal(e.error().vec_, expectedList));
}

// one arg
{
std::expected<int, Data<MoveOnly>> e(std::unexpect, {4, 5, 6}, MoveOnly(5));
assert(!e.has_value());
auto expectedList = {4, 5, 6};
assert((std::ranges::equal(e.error().vec_, expectedList)));
assert(std::get<0>(e.error().tuple_) == 5);
}

// multi args
{
int i = 5;
int j = 6;
MoveOnly m(7);
std::expected<int, Data<int&, int&&, MoveOnly>> e(std::unexpect, {1, 2}, i, std::move(j), std::move(m));
assert(!e.has_value());
auto expectedList = {1, 2};
assert((std::ranges::equal(e.error().vec_, expectedList)));
assert(&std::get<0>(e.error().tuple_) == &i);
assert(&std::get<1>(e.error().tuple_) == &j);
assert(std::get<2>(e.error().tuple_) == 7);
assert(m.get() == 0);
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing(std::initializer_list<int>, int) { throw Except{}; };
};

try {
std::expected<int, Throwing> u(std::unexpect, {1, 2}, 5);
assert(false);
} catch (Except) {
}
#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class G>
// constexpr explicit(!is_convertible_v<const G&, E>) expected(const unexpected<G>& e);
//
// Let GF be const G&
//
// Constraints: is_constructible_v<E, GF> is true.
//
// Effects: Direct-non-list-initializes unex with std::forward<GF>(e.error()).
//
// Postconditions: has_value() is false.
//
// Throws: Any exception thrown by the initialization of unex.

#include <cassert>
#include <expected>
#include <type_traits>
#include <utility>

#include "MoveOnly.h"
#include "test_macros.h"

// Test Constraints
static_assert(std::is_constructible_v<std::expected<int, int>, const std::unexpected<int>&>);

// !is_constructible_v<E, GF>
struct foo {};
static_assert(!std::is_constructible_v<std::expected<int, int>, const std::unexpected<foo>&>);
static_assert(!std::is_constructible_v<std::expected<int, MoveOnly>, const std::unexpected<MoveOnly>&>);

// explicit(!is_convertible_v<const G&, E>)
struct NotConvertible {
explicit NotConvertible(int);
};
static_assert(std::is_convertible_v<const std::unexpected<int>&, std::expected<int, int>>);
static_assert(!std::is_convertible_v<const std::unexpected<int>&, std::expected<int, NotConvertible>>);

struct MyInt {
int i;
constexpr MyInt(int ii) : i(ii) {}
friend constexpr bool operator==(const MyInt&, const MyInt&) = default;
};

template <class T>
constexpr void testUnexpected() {
const std::unexpected<int> u(5);
std::expected<int, T> e(u);
assert(!e.has_value());
assert(e.error() == 5);
}

constexpr bool test() {
testUnexpected<int>();
testUnexpected<MyInt>();
return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing(int) { throw Except{}; }
};

{
const std::unexpected<int> u(5);
try {
[[maybe_unused]] std::expected<int, Throwing> e(u);
assert(false);
} catch (Except) {
}
}

#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class G>
// constexpr explicit(!is_convertible_v<G, E>) expected(unexpected<G>&& e);
//
// Let GF be G
//
// Constraints: is_constructible_v<E, GF> is true.
//
// Effects: Direct-non-list-initializes unex with std::forward<GF>(e.error()).
//
// Postconditions: has_value() is false.
//
// Throws: Any exception thrown by the initialization of unex.

#include <cassert>
#include <expected>
#include <type_traits>
#include <utility>

#include "MoveOnly.h"
#include "test_macros.h"

// Test Constraints
static_assert(std::is_constructible_v<std::expected<int, int>, std::unexpected<int>>);
static_assert(std::is_constructible_v<std::expected<int, MoveOnly>, std::unexpected<MoveOnly>>);

// !is_constructible_v<E, GF>
struct foo {};
static_assert(!std::is_constructible_v<std::expected<int, int>, std::unexpected<foo>>);

// explicit(!is_convertible_v<G, E>)
struct NotConvertible {
explicit NotConvertible(int);
};
static_assert(std::is_convertible_v<std::unexpected<int>&&, std::expected<int, int>>);
static_assert(!std::is_convertible_v<std::unexpected<int>&&, std::expected<int, NotConvertible>>);

struct MyInt {
int i;
constexpr MyInt(int ii) : i(ii) {}
friend constexpr bool operator==(const MyInt&, const MyInt&) = default;
};

template <class Err>
constexpr void testInt() {
std::unexpected<int> u(5);
std::expected<int, Err> e(std::move(u));
assert(!e.has_value());
assert(e.error() == 5);
}

constexpr void testMoveOnly() {
std::unexpected<MoveOnly> u(MoveOnly(5));
std::expected<int, MoveOnly> e(std::move(u));
assert(!e.has_value());
assert(e.error() == 5);
assert(u.error() == 0);
}

constexpr bool test() {
testInt<int>();
testInt<MyInt>();
testInt<MoveOnly>();
testMoveOnly();
return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
struct Except {};

struct Throwing {
Throwing(int) { throw Except{}; }
};

{
std::unexpected<int> u(5);
try {
[[maybe_unused]] std::expected<int, Throwing> e(std::move(u));
assert(false);
} catch (Except) {
}
}

#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
68 changes: 68 additions & 0 deletions libcxx/test/std/utilities/expected/expected.expected/dtor.pass.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// Older Clangs do not support the C++20 feature to constrain destructors
// XFAIL: clang-14, apple-clang-14

// constexpr ~expected();
//
// Effects: If has_value() is true, destroys val, otherwise destroys unex.
//
// Remarks: If is_trivially_destructible_v<T> is true, and is_trivially_destructible_v<E> is true,
// then this destructor is a trivial destructor.

#include <cassert>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

// Test Remarks: If is_trivially_destructible_v<T> is true, and is_trivially_destructible_v<E> is true,
// then this destructor is a trivial destructor.
struct NonTrivial {
~NonTrivial() {}
};

#if __cpp_concepts >= 202002
static_assert(std::is_trivially_destructible_v<std::expected<int, int>>);
#endif
static_assert(!std::is_trivially_destructible_v<std::expected<NonTrivial, int>>);
static_assert(!std::is_trivially_destructible_v<std::expected<int, NonTrivial>>);
static_assert(!std::is_trivially_destructible_v<std::expected<NonTrivial, NonTrivial>>);

struct TrackedDestroy {
bool& destroyed;
constexpr TrackedDestroy(bool& b) : destroyed(b) {}
constexpr ~TrackedDestroy() { destroyed = true; }
};

constexpr bool test() {
// has value
{
bool valueDestroyed = false;
{ [[maybe_unused]] std::expected<TrackedDestroy, TrackedDestroy> e(std::in_place, valueDestroyed); }
assert(valueDestroyed);
}

// has error
{
bool errorDestroyed = false;
{ [[maybe_unused]] std::expected<TrackedDestroy, TrackedDestroy> e(std::unexpect, errorDestroyed); }
assert(errorDestroyed);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class T2> friend constexpr bool operator==(const expected& x, const T2& v);

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

struct Data {
int i;
constexpr Data(int ii) : i(ii) {}

friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; }
};

constexpr bool test() {
// x.has_value()
{
const std::expected<Data, int> e1(std::in_place, 5);
int i2 = 10;
int i3 = 5;
assert(e1 != i2);
assert(e1 == i3);
}

// !x.has_value()
{
const std::expected<Data, int> e1(std::unexpect, 5);
int i2 = 10;
int i3 = 5;
assert(e1 != i2);
assert(e1 != i3);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class T2, class E2> requires (!is_void_v<T2>)
// friend constexpr bool operator==(const expected& x, const expected<T2, E2>& y);

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

// Test constraint
template <class T1, class T2>
concept CanCompare = requires(T1 t1, T2 t2) { t1 == t2; };

struct Foo{};
static_assert(!CanCompare<Foo, Foo>);

static_assert(CanCompare<std::expected<int, int>, std::expected<int, int>>);
static_assert(CanCompare<std::expected<int, int>, std::expected<short, short>>);

// Note this is true because other overloads are unconstrained
static_assert(CanCompare<std::expected<int, int>, std::expected<void, int>>);

constexpr bool test() {
// x.has_value() && y.has_value()
{
const std::expected<int, int> e1(5);
const std::expected<int, int> e2(10);
const std::expected<int, int> e3(5);
assert(e1 != e2);
assert(e1 == e3);
}

// !x.has_value() && y.has_value()
{
const std::expected<int, int> e1(std::unexpect, 5);
const std::expected<int, int> e2(10);
const std::expected<int, int> e3(5);
assert(e1 != e2);
assert(e1 != e3);
}

// x.has_value() && !y.has_value()
{
const std::expected<int, int> e1(5);
const std::expected<int, int> e2(std::unexpect, 10);
const std::expected<int, int> e3(std::unexpect, 5);
assert(e1 != e2);
assert(e1 != e3);
}

// !x.has_value() && !y.has_value()
{
const std::expected<int, int> e1(std::unexpect, 5);
const std::expected<int, int> e2(std::unexpect, 10);
const std::expected<int, int> e3(std::unexpect, 5);
assert(e1 != e2);
assert(e1 == e3);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class E2> friend constexpr bool operator==(const expected& x, const unexpected<E2>& e);

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

struct Data {
int i;
constexpr Data(int ii) : i(ii) {}

friend constexpr bool operator==(const Data& data, int ii) { return data.i == ii; }
};

constexpr bool test() {
// x.has_value()
{
const std::expected<Data, Data> e1(std::in_place, 5);
std::unexpected<int> un2(10);
std::unexpected<int> un3(5);
assert(e1 != un2);
assert(e1 != un3);
}

// !x.has_value()
{
const std::expected<Data, Data> e1(std::unexpect, 5);
std::unexpected<int> un2(10);
std::unexpected<int> un3(5);
assert(e1 != un2);
assert(e1 == un3);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr const T* operator->() const noexcept;
// constexpr T* operator->() noexcept;

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

// Test noexcept
template <class T>
concept ArrowNoexcept =
requires(T t) {
{ t.operator->() } noexcept;
};

static_assert(!ArrowNoexcept<int>);

static_assert(ArrowNoexcept<std::expected<int, int>>);
static_assert(ArrowNoexcept<const std::expected<int, int>>);

constexpr bool test() {
// const
{
const std::expected<int, int> e(5);
std::same_as<const int*> decltype(auto) x = e.operator->();
assert(x == &(e.value()));
assert(*x == 5);
}

// non-const
{
std::expected<int, int> e(5);
std::same_as<int*> decltype(auto) x = e.operator->();
assert(x == &(e.value()));
assert(*x == 5);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr explicit operator bool() const noexcept;

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

// Test noexcept
template <class T>
concept OpBoolNoexcept =
requires(T t) {
{ static_cast<bool>(t) } noexcept;
};

struct Foo {};
static_assert(!OpBoolNoexcept<Foo>);

static_assert(OpBoolNoexcept<std::expected<int, int>>);
static_assert(OpBoolNoexcept<const std::expected<int, int>>);

// Test explicit
static_assert(!std::is_convertible_v<std::expected<int, int>, bool>);

constexpr bool test() {
// has_value
{
const std::expected<int, int> e(5);
assert(static_cast<bool>(e));
}

// !has_value
{
const std::expected<int, int> e(std::unexpect, 5);
assert(!static_cast<bool>(e));
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr const T& operator*() const & noexcept;
// constexpr T& operator*() & noexcept;
// constexpr T&& operator*() && noexcept;
// constexpr const T&& operator*() const && noexcept;

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

// Test noexcept
template <class T>
concept DerefNoexcept =
requires(T t) {
{ std::forward<T>(t).operator*() } noexcept;
};

static_assert(!DerefNoexcept<int>);

static_assert(DerefNoexcept<std::expected<int, int>&>);
static_assert(DerefNoexcept<const std::expected<int, int>&>);
static_assert(DerefNoexcept<std::expected<int, int>&&>);
static_assert(DerefNoexcept<const std::expected<int, int>&&>);

constexpr bool test() {
// non-const &
{
std::expected<int, int> e(5);
decltype(auto) x = *e;
static_assert(std::same_as<decltype(x), int&>);
assert(&x == &(e.value()));
assert(x == 5);
}

// const &
{
const std::expected<int, int> e(5);
decltype(auto) x = *e;
static_assert(std::same_as<decltype(x), const int&>);
assert(&x == &(e.value()));
assert(x == 5);
}

// non-const &&
{
std::expected<int, int> e(5);
decltype(auto) x = *std::move(e);
static_assert(std::same_as<decltype(x), int&&>);
assert(&x == &(e.value()));
assert(x == 5);
}

// const &&
{
const std::expected<int, int> e(5);
decltype(auto) x = *std::move(e);
static_assert(std::same_as<decltype(x), const int&&>);
assert(&x == &(e.value()));
assert(x == 5);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr const E& error() const & noexcept;
// constexpr E& error() & noexcept;
// constexpr E&& error() && noexcept;
// constexpr const E&& error() const && noexcept;

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

// Test noexcept
template <class T>
concept ErrorNoexcept =
requires(T t) {
{ std::forward<T>(t).error() } noexcept;
};

static_assert(!ErrorNoexcept<int>);

static_assert(ErrorNoexcept<std::expected<int, int>&>);
static_assert(ErrorNoexcept<const std::expected<int, int>&>);
static_assert(ErrorNoexcept<std::expected<int, int>&&>);
static_assert(ErrorNoexcept<const std::expected<int, int>&&>);

constexpr bool test() {
// non-const &
{
std::expected<int, int> e(std::unexpect, 5);
decltype(auto) x = e.error();
static_assert(std::same_as<decltype(x), int&>);
assert(x == 5);
}

// const &
{
const std::expected<int, int> e(std::unexpect, 5);
decltype(auto) x = e.error();
static_assert(std::same_as<decltype(x), const int&>);
assert(x == 5);
}

// non-const &&
{
std::expected<int, int> e(std::unexpect, 5);
decltype(auto) x = std::move(e).error();
static_assert(std::same_as<decltype(x), int&&>);
assert(x == 5);
}

// const &&
{
const std::expected<int, int> e(std::unexpect, 5);
decltype(auto) x = std::move(e).error();
static_assert(std::same_as<decltype(x), const int&&>);
assert(x == 5);
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr bool has_value() const noexcept;

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "test_macros.h"

// Test noexcept
template <class T>
concept HasValueNoexcept =
requires(T t) {
{ t.has_value() } noexcept;
};

struct Foo {};
static_assert(!HasValueNoexcept<Foo>);

static_assert(HasValueNoexcept<std::expected<int, int>>);
static_assert(HasValueNoexcept<const std::expected<int, int>>);

constexpr bool test() {
// has_value
{
const std::expected<int, int> e(5);
assert(e.has_value());
}

// !has_value
{
const std::expected<int, int> e(std::unexpect, 5);
assert(!e.has_value());
}

return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr const T& value() const &;
// constexpr T& value() &;
// constexpr T&& value() &&;
// constexpr const T&& value() const &&;

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "MoveOnly.h"
#include "test_macros.h"

constexpr bool test() {
// non-const &
{
std::expected<int, int> e(5);
decltype(auto) x = e.value();
static_assert(std::same_as<decltype(x), int&>);
assert(&x == &(*e));
assert(x == 5);
}

// const &
{
const std::expected<int, int> e(5);
decltype(auto) x = e.value();
static_assert(std::same_as<decltype(x), const int&>);
assert(&x == &(*e));
assert(x == 5);
}

// non-const &&
{
std::expected<int, int> e(5);
decltype(auto) x = std::move(e).value();
static_assert(std::same_as<decltype(x), int&&>);
assert(&x == &(*e));
assert(x == 5);
}

// const &&
{
const std::expected<int, int> e(5);
decltype(auto) x = std::move(e).value();
static_assert(std::same_as<decltype(x), const int&&>);
assert(&x == &(*e));
assert(x == 5);
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS

// int
{
const std::expected<int, int> e(std::unexpect, 5);
try {
e.value();
assert(false);
} catch (const std::bad_expected_access<int>& ex) {
assert(ex.error() == 5);
}
}

// MoveOnly
{
std::expected<int, MoveOnly> e(std::unexpect, 5);
try {
std::move(e).value();
assert(false);
} catch (const std::bad_expected_access<MoveOnly>& ex) {
assert(ex.error() == 5);
}
}

#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class U> constexpr T value_or(U&& v) const &;
// template<class U> constexpr T value_or(U&& v) &&;

#include <cassert>
#include <concepts>
#include <expected>
#include <type_traits>
#include <utility>

#include "MoveOnly.h"
#include "test_macros.h"

constexpr bool test() {
// const &, has_value()
{
const std::expected<int, int> e(5);
std::same_as<int> decltype(auto) x = e.value_or(10);
assert(x == 5);
}

// const &, !has_value()
{
const std::expected<int, int> e(std::unexpect, 5);
std::same_as<int> decltype(auto) x = e.value_or(10);
assert(x == 10);
}

// &&, has_value()
{
std::expected<MoveOnly, int> e(std::in_place, 5);
std::same_as<MoveOnly> decltype(auto) x = std::move(e).value_or(10);
assert(x == 5);
}

// &&, !has_value()
{
std::expected<MoveOnly, int> e(std::unexpect, 5);
std::same_as<MoveOnly> decltype(auto) x = std::move(e).value_or(10);
assert(x == 10);
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS

// int
{
const std::expected<int, int> e(std::unexpect, 5);
try {
e.value();
assert(false);
} catch (const std::bad_expected_access<int>& ex) {
assert(ex.error() == 5);
}
}

// MoveOnly
{
std::expected<int, MoveOnly> e(std::unexpect, 5);
try {
std::move(e).value();
assert(false);
} catch (const std::bad_expected_access<MoveOnly>& ex) {
assert(ex.error() == 5);
}
}

#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// Older Clangs do not support the C++20 feature to constrain destructors
// XFAIL: clang-14, clang-15, apple-clang-14

// friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y)));

#include <cassert>
#include <expected>
#include <type_traits>
#include <utility>

#include "../../types.h"
#include "test_macros.h"

// Test Constraints:
struct NotSwappable {
NotSwappable operator=(const NotSwappable&) = delete;
};
void swap(NotSwappable&, NotSwappable&) = delete;

static_assert(std::is_swappable_v<std::expected<int, int>>);

// !is_swappable_v<T>
static_assert(!std::is_swappable_v<std::expected<NotSwappable, int>>);

// !is_swappable_v<E>
static_assert(!std::is_swappable_v<std::expected<int, NotSwappable>>);

struct NotMoveContructible {
NotMoveContructible(NotMoveContructible&&) = delete;
friend void swap(NotMoveContructible&, NotMoveContructible&) {}
};

// !is_move_constructible_v<T>
static_assert(!std::is_swappable_v<std::expected<NotMoveContructible, int>>);

// !is_move_constructible_v<E>
static_assert(!std::is_swappable_v<std::expected<int, NotMoveContructible>>);

struct MoveMayThrow {
MoveMayThrow(MoveMayThrow&&) noexcept(false);
friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {}
};

// !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>
static_assert(std::is_swappable_v<std::expected<MoveMayThrow, int>>);

// is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
static_assert(std::is_swappable_v<std::expected<int, MoveMayThrow>>);

// !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
static_assert(!std::is_swappable_v<std::expected<MoveMayThrow, MoveMayThrow>>);

// Test noexcept
static_assert(std::is_nothrow_swappable_v<std::expected<int, int>>);

// !is_nothrow_move_constructible_v<T>
static_assert(!std::is_nothrow_swappable_v<std::expected<MoveMayThrow, int>>);

// !is_nothrow_move_constructible_v<E>
static_assert(!std::is_nothrow_swappable_v<std::expected<int, MoveMayThrow>>);

struct SwapMayThrow {
friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}
};

// !is_nothrow_swappable_v<T>
static_assert(!std::is_nothrow_swappable_v<std::expected<SwapMayThrow, int>>);

// !is_nothrow_swappable_v<E>
static_assert(!std::is_nothrow_swappable_v<std::expected<int, SwapMayThrow>>);

constexpr bool test() {
// this->has_value() && rhs.has_value()
{
std::expected<ADLSwap, int> x(std::in_place, 5);
std::expected<ADLSwap, int> y(std::in_place, 10);
swap(x, y);

assert(x.has_value());
assert(x->i == 10);
assert(x->adlSwapCalled);
assert(y.has_value());
assert(y->i == 5);
assert(y->adlSwapCalled);
}

// !this->has_value() && !rhs.has_value()
{
std::expected<int, ADLSwap> x(std::unexpect, 5);
std::expected<int, ADLSwap> y(std::unexpect, 10);
swap(x, y);

assert(!x.has_value());
assert(x.error().i == 10);
assert(x.error().adlSwapCalled);
assert(!y.has_value());
assert(y.error().i == 5);
assert(y.error().adlSwapCalled);
}

// this->has_value() && !rhs.has_value()
// && is_nothrow_move_constructible_v<E>
{
std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::in_place, 5);
std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::unexpect, 10);

swap(e1, e2);

assert(!e1.has_value());
assert(e1.error().i == 10);
assert(e2.has_value());
assert(e2->i == 5);

assert(e1.error().numberOfMoves == 2);
assert(!e1.error().swapCalled);
assert(e2->numberOfMoves == 1);
assert(!e2->swapCalled);
}

// this->has_value() && !rhs.has_value()
// && !is_nothrow_move_constructible_v<E>
{
std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::in_place, 5);
std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::unexpect, 10);

e1.swap(e2);

assert(!e1.has_value());
assert(e1.error().i == 10);
assert(e2.has_value());
assert(e2->i == 5);

assert(e1.error().numberOfMoves == 1);
assert(!e1.error().swapCalled);
assert(e2->numberOfMoves == 2);
assert(!e2->swapCalled);
}

// !this->has_value() && rhs.has_value()
// && is_nothrow_move_constructible_v<E>
{
std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::unexpect, 10);
std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::in_place, 5);

swap(e1, e2);

assert(e1.has_value());
assert(e1->i == 5);
assert(!e2.has_value());
assert(e2.error().i == 10);

assert(e1->numberOfMoves == 1);
assert(!e1->swapCalled);
assert(e2.error().numberOfMoves == 2);
assert(!e2.error().swapCalled);
}

// !this->has_value() && rhs.has_value()
// && !is_nothrow_move_constructible_v<E>
{
std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::unexpect, 10);
std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::in_place, 5);

swap(e1, e2);

assert(e1.has_value());
assert(e1->i == 5);
assert(!e2.has_value());
assert(e2.error().i == 10);

assert(e1->numberOfMoves == 2);
assert(!e1->swapCalled);
assert(e2.error().numberOfMoves == 1);
assert(!e2.error().swapCalled);
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
// !e1.has_value() && e2.has_value()
{
std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5);
std::expected<ThrowOnMoveConstruct, int> e2(std::in_place);
try {
swap(e1, e2);
assert(false);
} catch (Except) {
assert(!e1.has_value());
assert(e1.error() == 5);
}
}

// e1.has_value() && !e2.has_value()
{
std::expected<int, ThrowOnMoveConstruct> e1(5);
std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect);
try {
swap(e1, e2);
assert(false);
} catch (Except) {
assert(e1.has_value());
assert(*e1 == 5);
}
}
#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr void swap(expected& rhs) noexcept(see below);
//
// Constraints:
// is_swappable_v<T> is true and
// is_swappable_v<E> is true and
// is_move_constructible_v<T> && is_move_constructible_v<E> is true, and
// is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true.
//
// Throws: Any exception thrown by the expressions in the Effects.
// Remarks: The exception specification is equivalent to:
// is_nothrow_move_constructible_v<T> && is_nothrow_swappable_v<T> &&
// is_nothrow_move_constructible_v<E> && is_nothrow_swappable_v<E>

#include <cassert>
#include <expected>
#include <type_traits>
#include <utility>

#include "../../types.h"
#include "test_macros.h"

// Test Constraints:
template <class T, class E>
concept HasMemberSwap = requires(std::expected<T, E> x, std::expected<T, E> y) { x.swap(y); };

static_assert(HasMemberSwap<int, int>);

struct NotSwappable {};
void swap(NotSwappable&, NotSwappable&) = delete;

// !is_swappable_v<T>
static_assert(!HasMemberSwap<NotSwappable, int>);

// !is_swappable_v<E>
static_assert(!HasMemberSwap<int, NotSwappable>);

struct NotMoveContructible {
NotMoveContructible(NotMoveContructible&&) = delete;
friend void swap(NotMoveContructible&, NotMoveContructible&) {}
};

// !is_move_constructible_v<T>
static_assert(!HasMemberSwap<NotMoveContructible, int>);

// !is_move_constructible_v<E>
static_assert(!HasMemberSwap<int, NotMoveContructible>);

struct MoveMayThrow {
MoveMayThrow(MoveMayThrow&&) noexcept(false);
friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {}
};

// !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>
static_assert(HasMemberSwap<MoveMayThrow, int>);

// is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
static_assert(HasMemberSwap<int, MoveMayThrow>);

// !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E>
static_assert(!HasMemberSwap<MoveMayThrow, MoveMayThrow>);

// Test noexcept
template <class T, class E>
concept MemberSwapNoexcept =
requires(std::expected<T, E> x, std::expected<T, E> y) {
{ x.swap(y) } noexcept;
};

static_assert(MemberSwapNoexcept<int, int>);

// !is_nothrow_move_constructible_v<T>
static_assert(!MemberSwapNoexcept<MoveMayThrow, int>);

// !is_nothrow_move_constructible_v<E>
static_assert(!MemberSwapNoexcept<int, MoveMayThrow>);

struct SwapMayThrow {
friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {}
};

// !is_nothrow_swappable_v<T>
static_assert(!MemberSwapNoexcept<SwapMayThrow, int>);

// !is_nothrow_swappable_v<E>
static_assert(!MemberSwapNoexcept<int, SwapMayThrow>);

constexpr bool test() {
// this->has_value() && rhs.has_value()
{
std::expected<ADLSwap, int> x(std::in_place, 5);
std::expected<ADLSwap, int> y(std::in_place, 10);
x.swap(y);

assert(x.has_value());
assert(x->i == 10);
assert(x->adlSwapCalled);
assert(y.has_value());
assert(y->i == 5);
assert(y->adlSwapCalled);
}

// !this->has_value() && !rhs.has_value()
{
std::expected<int, ADLSwap> x(std::unexpect, 5);
std::expected<int, ADLSwap> y(std::unexpect, 10);
x.swap(y);

assert(!x.has_value());
assert(x.error().i == 10);
assert(x.error().adlSwapCalled);
assert(!y.has_value());
assert(y.error().i == 5);
assert(y.error().adlSwapCalled);
}

// this->has_value() && !rhs.has_value()
// && is_nothrow_move_constructible_v<E>
{
std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::in_place, 5);
std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::unexpect, 10);

e1.swap(e2);

assert(!e1.has_value());
assert(e1.error().i == 10);
assert(e2.has_value());
assert(e2->i == 5);

assert(e1.error().numberOfMoves == 2);
assert(!e1.error().swapCalled);
assert(e2->numberOfMoves == 1);
assert(!e2->swapCalled);
}

// this->has_value() && !rhs.has_value()
// && !is_nothrow_move_constructible_v<E>
{
std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::in_place, 5);
std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::unexpect, 10);

e1.swap(e2);

assert(!e1.has_value());
assert(e1.error().i == 10);
assert(e2.has_value());
assert(e2->i == 5);

assert(e1.error().numberOfMoves == 1);
assert(!e1.error().swapCalled);
assert(e2->numberOfMoves == 2);
assert(!e2->swapCalled);
}

// !this->has_value() && rhs.has_value()
// && is_nothrow_move_constructible_v<E>
{
std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::unexpect, 10);
std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::in_place, 5);

e1.swap(e2);

assert(e1.has_value());
assert(e1->i == 5);
assert(!e2.has_value());
assert(e2.error().i == 10);

assert(e1->numberOfMoves == 1);
assert(!e1->swapCalled);
assert(e2.error().numberOfMoves == 2);
assert(!e2.error().swapCalled);
}

// !this->has_value() && rhs.has_value()
// && !is_nothrow_move_constructible_v<E>
{
std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::unexpect, 10);
std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::in_place, 5);

e1.swap(e2);

assert(e1.has_value());
assert(e1->i == 5);
assert(!e2.has_value());
assert(e2.error().i == 10);

assert(e1->numberOfMoves == 2);
assert(!e1->swapCalled);
assert(e2.error().numberOfMoves == 1);
assert(!e2.error().swapCalled);
}

return true;
}

void testException() {
#ifndef TEST_HAS_NO_EXCEPTIONS
// !e1.has_value() && e2.has_value()
{
std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5);
std::expected<ThrowOnMoveConstruct, int> e2(std::in_place);
try {
e1.swap(e2);
assert(false);
} catch (Except) {
assert(!e1.has_value());
assert(e1.error() == 5);
}
}

// e1.has_value() && !e2.has_value()
{
std::expected<int, ThrowOnMoveConstruct> e1(5);
std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect);
try {
e1.swap(e2);
assert(false);
} catch (Except) {
assert(e1.has_value());
assert(*e1 == 5);
}
}
#endif // TEST_HAS_NO_EXCEPTIONS
}

int main(int, char**) {
test();
static_assert(test());
testException();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr unexpected& operator=(const unexpected&) = default;

#include <cassert>
#include <expected>

struct Error {
int i;
constexpr Error(int ii) : i(ii) {}
};

constexpr bool test() {
std::unexpected<Error> unex1(4);
const std::unexpected<Error> unex2(5);
unex1 = unex2;
assert(unex1.error().i == 5);
return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr unexpected& operator=(unexpected&&) = default;

#include <cassert>
#include <expected>
#include <utility>

struct Error {
int i;
constexpr Error(int ii) : i(ii) {}
constexpr Error& operator=(Error&& other) {
i = other.i;
other.i = 0;
return *this;
}
};

constexpr bool test() {
std::unexpected<Error> unex1(4);
std::unexpected<Error> unex2(5);
unex1 = std::move(unex2);
assert(unex1.error().i == 5);
assert(unex2.error().i == 0);
return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// template<class E> unexpected(E) -> unexpected<E>;

#include <concepts>
#include <expected>

struct Foo{};

static_assert(std::same_as<decltype(std::unexpected(5)), std::unexpected<int>>);
static_assert(std::same_as<decltype(std::unexpected(Foo{})), std::unexpected<Foo>>);
static_assert(std::same_as<decltype(std::unexpected(std::unexpected<int>(5))), std::unexpected<int>>);
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===----------------------------------------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20

// constexpr unexpected(const unexpected&) = default;

#include <cassert>
#include <expected>

struct Error {
int i;
constexpr Error(int ii) : i(ii) {}
};

constexpr bool test() {
const std::unexpected<Error> unex(5);
auto unex2 = unex;
assert(unex2.error().i == 5);
return true;
}

int main(int, char**) {
test();
static_assert(test());
return 0;
}
Loading